Merge remote-tracking branch 'upstream/master' into versioninfo

actorid
pvdk 11 years ago
commit f263acdac6

@ -236,7 +236,9 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
// Loop through all the references
ESM::CellRef ref;
if(!quiet) std::cout << " References:\n";
while(cell.getNextRef(esm, ref))
bool deleted = false;
while(cell.getNextRef(esm, ref, deleted))
{
if (save) {
info.data.mCellRefs[&cell].push_back(ref);
@ -244,13 +246,14 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
if(quiet) continue;
std::cout << " Refnum: " << ref.mRefnum << std::endl;
std::cout << " Refnum: " << ref.mRefNum.mIndex << std::endl;
std::cout << " ID: '" << ref.mRefID << "'\n";
std::cout << " Owner: '" << ref.mOwner << "'\n";
std::cout << " Enchantment charge: '" << ref.mEnchantmentCharge << "'\n";
std::cout << " Uses/health: '" << ref.mCharge << "'\n";
std::cout << " Gold value: '" << ref.mGoldValue << "'\n";
std::cout << " Blocked: '" << static_cast<int>(ref.mReferenceBlocked) << "'" << std::endl;
std::cout << " Deleted: " << deleted << std::endl;
}
}

@ -133,16 +133,10 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, std::vector<std::
state==CSMWorld::RecordBase::State_ModifiedOnly ||
infoModified)
{
// always write the topic record
std::string type;
for (int i=0; i<4; ++i)
/// \todo make endianess agnostic (change ESMWriter interface?)
type += reinterpret_cast<const char *> (&topic.mModified.sRecordId)[i];
mState.getWriter().startRecord (type);
mState.getWriter().startRecord (topic.mModified.sRecordId);
mState.getWriter().writeHNCString ("NAME", topic.mModified.mId);
topic.mModified.save (mState.getWriter());
mState.getWriter().endRecord (type);
mState.getWriter().endRecord (topic.mModified.sRecordId);
// write modified selected info records
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second;
@ -178,15 +172,10 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, std::vector<std::
next->mModified.mId.substr (next->mModified.mId.find_last_of ('#')+1);
}
std::string type;
for (int i=0; i<4; ++i)
/// \todo make endianess agnostic (change ESMWriter interface?)
type += reinterpret_cast<const char *> (&info.sRecordId)[i];
mState.getWriter().startRecord (type);
mState.getWriter().startRecord (info.sRecordId);
mState.getWriter().writeHNCString ("INAM", info.mId);
info.save (mState.getWriter());
mState.getWriter().endRecord (type);
mState.getWriter().endRecord (info.sRecordId);
}
}
}

@ -104,10 +104,10 @@ namespace CSMDoc
/// \todo make endianess agnostic (change ESMWriter interface?)
type += reinterpret_cast<const char *> (&mCollection.getRecord (stage).mModified.sRecordId)[i];
mState.getWriter().startRecord (type);
mState.getWriter().startRecord (mCollection.getRecord (stage).mModified.sRecordId);
mState.getWriter().writeHNCString ("NAME", mCollection.getId (stage));
mCollection.getRecord (stage).mModified.save (mState.getWriter());
mState.getWriter().endRecord (type);
mState.getWriter().endRecord (mCollection.getRecord (stage).mModified.sRecordId);
}
else if (state==CSMWorld::RecordBase::State_Deleted)
{

@ -98,6 +98,10 @@ namespace CSMWorld
UniversalId::Type type = UniversalId::Type_None);
///< \param type Will be ignored, unless the collection supports multiple record types
virtual void cloneRecord(const std::string& origin,
const std::string& destination,
const UniversalId::Type type);
virtual int searchId (const std::string& id) const;
////< Search record with \a id.
/// \return index of record (if found) or -1 (not found)
@ -193,6 +197,19 @@ namespace CSMWorld
return true;
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::cloneRecord(const std::string& origin,
const std::string& destination,
const UniversalId::Type type)
{
Record<ESXRecordT> copy;
copy.mModified = getRecord(origin).get();
copy.mState = RecordBase::State_ModifiedOnly;
copy.get().mId = destination;
insertRecord(copy, getAppendIndex(destination, type));
}
template<typename ESXRecordT, typename IdAccessorT>
Collection<ESXRecordT, IdAccessorT>::Collection()
{}

@ -74,6 +74,10 @@ namespace CSMWorld
UniversalId::Type type = UniversalId::Type_None) = 0;
///< If the record type does not match, an exception is thrown.
virtual void cloneRecord(const std::string& origin,
const std::string& destination,
const UniversalId::Type type) = 0;
virtual const RecordBase& getRecord (const std::string& id) const = 0;
virtual const RecordBase& getRecord (int index) const = 0;

@ -4,10 +4,10 @@
#include <QAbstractItemModel>
#include "idtable.hpp"
#include "idtable.hpp"
#include <components/misc/stringops.hpp>
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_)
{
mOld = mModel.data (mIndex, Qt::EditRole);
@ -25,7 +25,7 @@ void CSMWorld::ModifyCommand::undo()
mModel.setData (mIndex, mOld);
}
CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand *parent)
CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand* parent)
: QUndoCommand (parent), mModel (model), mId (id), mType (UniversalId::Type_None)
{
setText (("Create record " + id).c_str());
@ -54,7 +54,7 @@ void CSMWorld::CreateCommand::undo()
mModel.removeRow (mModel.getModelIndex (mId, 0).row());
}
CSMWorld::RevertCommand::RevertCommand (IdTable& model, const std::string& id, QUndoCommand *parent)
CSMWorld::RevertCommand::RevertCommand (IdTable& model, const std::string& id, QUndoCommand* parent)
: QUndoCommand (parent), mModel (model), mId (id), mOld (0)
{
setText (("Revert record " + id).c_str());
@ -89,7 +89,7 @@ void CSMWorld::RevertCommand::undo()
mModel.setRecord (mId, *mOld);
}
CSMWorld::DeleteCommand::DeleteCommand (IdTable& model, const std::string& id, QUndoCommand *parent)
CSMWorld::DeleteCommand::DeleteCommand (IdTable& model, const std::string& id, QUndoCommand* parent)
: QUndoCommand (parent), mModel (model), mId (id), mOld (0)
{
setText (("Delete record " + id).c_str());
@ -126,7 +126,7 @@ void CSMWorld::DeleteCommand::undo()
CSMWorld::ReorderRowsCommand::ReorderRowsCommand (IdTable& model, int baseIndex,
const std::vector<int>& newOrder)
const std::vector<int>& newOrder)
: mModel (model), mBaseIndex (baseIndex), mNewOrder (newOrder)
{}
@ -140,8 +140,35 @@ void CSMWorld::ReorderRowsCommand::undo()
int size = static_cast<int> (mNewOrder.size());
std::vector<int> reverse (size);
for (int i=0; i<size; ++i)
for (int i=0; i< size; ++i)
reverse.at (mNewOrder[i]) = i;
mModel.reorderRows (mBaseIndex, reverse);
}
CSMWorld::CloneCommand::CloneCommand (CSMWorld::IdTable& model,
const std::string& idOrigin,
const std::string& IdDestination,
const CSMWorld::UniversalId::Type type,
QUndoCommand* parent) :
QUndoCommand (parent),
mModel (model),
mIdOrigin (idOrigin),
mIdDestination (Misc::StringUtils::lowerCase (IdDestination)),
mType (type)
{
setText ( ("Clone record " + idOrigin + " to the " + IdDestination).c_str());
}
void CSMWorld::CloneCommand::redo()
{
mModel.cloneRecord (mIdOrigin, mIdDestination, mType);
for (std::map<int, QVariant>::const_iterator iter (mValues.begin()); iter != mValues.end(); ++iter)
mModel.setData (mModel.getModelIndex (mIdDestination, iter->first), iter->second);
}
void CSMWorld::CloneCommand::undo()
{
mModel.removeRow (mModel.getModelIndex (mIdDestination, 0).row());
}

@ -39,6 +39,26 @@ namespace CSMWorld
virtual void undo();
};
class CloneCommand : public QUndoCommand
{
IdTable& mModel;
std::string mIdOrigin;
std::string mIdDestination;
UniversalId::Type mType;
std::map<int, QVariant> mValues;
public:
CloneCommand (IdTable& model, const std::string& idOrigin,
const std::string& IdDestination,
const UniversalId::Type type,
QUndoCommand* parent = 0);
virtual void redo();
virtual void undo();
};
class CreateCommand : public QUndoCommand
{
IdTable& mModel;

@ -154,14 +154,17 @@ CSMWorld::Data::Data() : mRefs (mCells)
mTopics.addColumn (new StringIdColumn<ESM::Dialogue>);
mTopics.addColumn (new RecordStateColumn<ESM::Dialogue>);
mTopics.addColumn (new FixedRecordTypeColumn<ESM::Dialogue> (UniversalId::Type_Topic));
mTopics.addColumn (new DialogueTypeColumn<ESM::Dialogue>);
mJournals.addColumn (new StringIdColumn<ESM::Dialogue>);
mJournals.addColumn (new RecordStateColumn<ESM::Dialogue>);
mJournals.addColumn (new FixedRecordTypeColumn<ESM::Dialogue> (UniversalId::Type_Journal));
mJournals.addColumn (new DialogueTypeColumn<ESM::Dialogue> (true));
mTopicInfos.addColumn (new StringIdColumn<Info> (true));
mTopicInfos.addColumn (new RecordStateColumn<Info>);
mTopicInfos.addColumn (new FixedRecordTypeColumn<Info> (UniversalId::Type_TopicInfo));
mTopicInfos.addColumn (new TopicColumn<Info> (false));
mTopicInfos.addColumn (new ActorColumn<Info>);
mTopicInfos.addColumn (new RaceColumn<Info>);
@ -178,6 +181,7 @@ CSMWorld::Data::Data() : mRefs (mCells)
mJournalInfos.addColumn (new StringIdColumn<Info> (true));
mJournalInfos.addColumn (new RecordStateColumn<Info>);
mJournalInfos.addColumn (new FixedRecordTypeColumn<Info> (UniversalId::Type_Journal));
mJournalInfos.addColumn (new TopicColumn<Info> (true));
mJournalInfos.addColumn (new QuestStatusTypeColumn<Info>);
mJournalInfos.addColumn (new QuestIndexColumn<Info>);
@ -194,6 +198,7 @@ CSMWorld::Data::Data() : mRefs (mCells)
mRefs.addColumn (new StringIdColumn<CellRef> (true));
mRefs.addColumn (new RecordStateColumn<CellRef>);
mRefs.addColumn (new FixedRecordTypeColumn<CellRef> (UniversalId::Type_Reference));
mRefs.addColumn (new CellColumn<CellRef>);
mRefs.addColumn (new IdColumn<CellRef>);
mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mPos, 0, false));
@ -224,6 +229,7 @@ CSMWorld::Data::Data() : mRefs (mCells)
mFilters.addColumn (new StringIdColumn<CSMFilter::Filter>);
mFilters.addColumn (new RecordStateColumn<CSMFilter::Filter>);
mFilters.addColumn (new FixedRecordTypeColumn<CSMFilter::Filter> (UniversalId::Type_Filter));
mFilters.addColumn (new FilterColumn<CSMFilter::Filter>);
mFilters.addColumn (new DescriptionColumn<CSMFilter::Filter>);
mFilters.addColumn (new ScopeColumn<CSMFilter::Filter>);

@ -124,6 +124,17 @@ void CSMWorld::IdTable::addRecord (const std::string& id, UniversalId::Type type
endInsertRows();
}
void CSMWorld::IdTable::cloneRecord(const std::string& origin,
const std::string& destination,
CSMWorld::UniversalId::Type type)
{
int index = mIdCollection->getAppendIndex (destination);
beginInsertRows (QModelIndex(), index, index);
mIdCollection->cloneRecord(origin, destination, type);
endInsertRows();
}
QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const
{
return index (mIdCollection->getIndex (id), column);

@ -63,6 +63,10 @@ namespace CSMWorld
void addRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None);
///< \param type Will be ignored, unless the collection supports multiple record types
void cloneRecord(const std::string& origin,
const std::string& destination,
UniversalId::Type type = UniversalId::Type_None);
QModelIndex getModelIndex (const std::string& id, int column) const;
void setRecord (const std::string& id, const RecordBase& record);

@ -8,6 +8,5 @@ void CSMWorld::CellRef::load (ESM::ESMReader &esm, Cell& cell, const std::string
mId = id;
mCell = cell.mId;
if (!mDeleted)
cell.addRef (mId);
cell.addRef (mId);
}

@ -5,6 +5,8 @@
#include "ref.hpp"
#include "cell.hpp"
#include "universalid.hpp"
#include "record.hpp"
void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base)
{
@ -14,7 +16,8 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
CellRef ref;
while (cell2.getNextRef (reader, ref))
bool deleted = false;
while (cell2.getNextRef (reader, ref, deleted))
{
/// \todo handle deleted and moved references
ref.load (reader, cell2, getNewId());
@ -34,4 +37,15 @@ std::string CSMWorld::RefCollection::getNewId()
std::ostringstream stream;
stream << "ref#" << mNextId++;
return stream.str();
}
void CSMWorld::RefCollection::cloneRecord(const std::string& origin,
const std::string& destination,
const CSMWorld::UniversalId::Type type,
const CSMWorld::UniversalId::ArgumentType argumentType)
{
Record<CSMWorld::CellRef> clone(getRecord(origin));
clone.mState = CSMWorld::RecordBase::State_ModifiedOnly;
clone.get().mId = destination;
insertRecord(clone, getAppendIndex(destination, type), type);
}

@ -8,6 +8,7 @@
namespace CSMWorld
{
struct Cell;
struct UniversalId;
/// \brief References in cells
class RefCollection : public Collection<CellRef>
@ -25,6 +26,11 @@ namespace CSMWorld
///< Load a sequence of references.
std::string getNewId();
void cloneRecord(const std::string& origin,
const std::string& destination,
const CSMWorld::UniversalId::Type type,
const CSMWorld::UniversalId::ArgumentType argumentType);
};
}

@ -31,6 +31,7 @@ namespace CSMWorld
///< If the data type does not match an exception is thrown.
virtual std::string getId (const RecordBase& record) const = 0;
virtual void setId(RecordBase& record, const std::string& id) = 0;
};
}

@ -34,6 +34,8 @@ namespace CSMWorld
BaseRefIdAdapter (UniversalId::Type type, const BaseColumns& base);
virtual std::string getId (const RecordBase& record) const;
virtual void setId (RecordBase& record, const std::string& id);
virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)
const;
@ -50,6 +52,12 @@ namespace CSMWorld
: mType (type), mBase (base)
{}
template<typename RecordT>
void BaseRefIdAdapter<RecordT>::setId (RecordBase& record, const std::string& id)
{
(dynamic_cast<Record<RecordT>&> (record).get().mId) = id;
}
template<typename RecordT>
std::string BaseRefIdAdapter<RecordT>::getId (const RecordBase& record) const
{

@ -2,6 +2,7 @@
#include "refidcollection.hpp"
#include <stdexcept>
#include <memory>
#include <components/esm/esmreader.hpp>
@ -449,6 +450,16 @@ void CSMWorld::RefIdCollection::replace (int index, const RecordBase& record)
mData.getRecord (mData.globalToLocalIndex (index)).assign (record);
}
void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin,
const std::string& destination,
const CSMWorld::UniversalId::Type type)
{
std::auto_ptr<RecordBase> newRecord(mData.getRecord(mData.searchId(origin)).clone());
newRecord->mState = RecordBase::State_ModifiedOnly;
mAdapters.find(type)->second->setId(*newRecord, destination);
mData.insertRecord(*newRecord, type, destination);
}
void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record,
UniversalId::Type type)
{

@ -69,6 +69,10 @@ namespace CSMWorld
virtual void removeRows (int index, int count);
virtual void cloneRecord(const std::string& origin,
const std::string& destination,
const 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

@ -330,4 +330,18 @@ const CSMWorld::RefIdDataContainer< ESM::Repair >& CSMWorld::RefIdData::getRepai
const CSMWorld::RefIdDataContainer< ESM::Static >& CSMWorld::RefIdData::getStatics() const
{
return mStatics;
}
}
void CSMWorld::RefIdData::insertRecord(CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type, const std::string& id)
{
std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter =
mRecordContainers.find (type);
if (iter==mRecordContainers.end())
throw std::logic_error ("invalid local index type");
iter->second->insertRecord(record);
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id),
LocalIndex (iter->second->getSize()-1, type)));
}

@ -46,6 +46,8 @@ namespace CSMWorld
virtual RecordBase& getRecord (int index)= 0;
virtual void appendRecord (const std::string& id) = 0;
virtual void insertRecord (RecordBase& record) = 0;
virtual void load (int index, ESM::ESMReader& reader, bool base) = 0;
@ -68,6 +70,8 @@ namespace CSMWorld
virtual RecordBase& getRecord (int index);
virtual void appendRecord (const std::string& id);
virtual void insertRecord (RecordBase& record);
virtual void load (int index, ESM::ESMReader& reader, bool base);
@ -78,6 +82,13 @@ namespace CSMWorld
virtual void save (int index, ESM::ESMWriter& writer) const;
};
template<typename RecordT>
void RefIdDataContainer<RecordT>::insertRecord(RecordBase& record)
{
Record<RecordT>& newRecord = dynamic_cast<Record<RecordT>& >(record);
mContainer.push_back(newRecord);
}
template<typename RecordT>
int RefIdDataContainer<RecordT>::getSize() const
{
@ -136,15 +147,10 @@ namespace CSMWorld
if (state==CSMWorld::RecordBase::State_Modified ||
state==CSMWorld::RecordBase::State_ModifiedOnly)
{
std::string type;
for (int i=0; i<4; ++i)
/// \todo make endianess agnostic (change ESMWriter interface?)
type += reinterpret_cast<const char *> (&mContainer.at (index).mModified.sRecordId)[i];
writer.startRecord (type);
writer.startRecord (mContainer.at (index).mModified.sRecordId);
writer.writeHNCString ("NAME", getId (index));
mContainer.at (index).mModified.save (writer);
writer.endRecord (type);
writer.endRecord (mContainer.at (index).mModified.sRecordId);
}
else if (state==CSMWorld::RecordBase::State_Deleted)
{
@ -200,6 +206,8 @@ namespace CSMWorld
LocalIndex searchId (const std::string& id) const;
void erase (int index, int count);
void insertRecord(CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type, const std::string& id);
const RecordBase& getRecord (const LocalIndex& index) const;

@ -61,6 +61,7 @@ void CSVWorld::CellCreator::reset()
mX->setValue (0);
mY->setValue (0);
mType->setCurrentIndex (0);
setType(0);
GenericCreator::reset();
}
@ -78,4 +79,25 @@ void CSVWorld::CellCreator::setType (int index)
void CSVWorld::CellCreator::valueChanged (int index)
{
update();
}
}
void CSVWorld::CellCreator::cloneMode(const std::string& originId,
const CSMWorld::UniversalId::Type type)
{
CSVWorld::GenericCreator::cloneMode(originId, type);
if (*(originId.begin()) == '#') //if originid points to the exterior cell
{
setType(1); //enable x and y controls
mType->setCurrentIndex(1);
} else {
setType(0);
mType->setCurrentIndex(0);
}
}
void CSVWorld::CellCreator::toggleWidgets(bool active)
{
CSVWorld::GenericCreator::toggleWidgets(active);
mType->setEnabled(active);
}

@ -29,6 +29,11 @@ namespace CSVWorld
virtual void reset();
virtual void toggleWidgets(bool active = true);
virtual void cloneMode(const std::string& originId,
const CSMWorld::UniversalId::Type type);
private slots:
void setType (int index);

@ -2,6 +2,7 @@
#define CSV_WORLD_CREATOR_H
#include <QWidget>
#include "../../model/world/universalid.hpp"
class QUndoStack;
@ -24,8 +25,13 @@ namespace CSVWorld
virtual void reset() = 0;
virtual void cloneMode(const std::string& originId,
const CSMWorld::UniversalId::Type type) = 0;
virtual void setEditLock (bool locked) = 0;
virtual void toggleWidgets(bool active = true) = 0;
signals:
void done();

@ -57,8 +57,15 @@ const CSMWorld::UniversalId& CSVWorld::GenericCreator::getCollectionId() const
}
CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id, bool relaxedIdRules)
: mData (data), mUndoStack (undoStack), mListId (id), mLocked (false)
const CSMWorld::UniversalId& id, bool relaxedIdRules):
mData (data),
mUndoStack (undoStack),
mListId (id),
mLocked (false),
mCloneMode(false),
mClonedType(CSMWorld::UniversalId::Type_None)
{
mLayout = new QHBoxLayout;
mLayout->setContentsMargins (0, 0, 0, 0);
@ -89,6 +96,7 @@ void CSVWorld::GenericCreator::setEditLock (bool locked)
void CSVWorld::GenericCreator::reset()
{
mCloneMode = false;
mId->setText ("");
update();
}
@ -120,16 +128,40 @@ void CSVWorld::GenericCreator::create()
{
if (!mLocked)
{
std::string id = getId();
std::auto_ptr<CSMWorld::CreateCommand> command (new CSMWorld::CreateCommand (
if (mCloneMode)
{
std::string id = getId();
std::auto_ptr<CSMWorld::CloneCommand> command (new CSMWorld::CloneCommand (
dynamic_cast<CSMWorld::IdTable&> (*mData.getTableModel(mListId)), mClonedId, id, mClonedType));
mUndoStack.push(command.release());
emit done();
emit requestFocus(id);
} else {
std::string id = getId();
std::auto_ptr<CSMWorld::CreateCommand> command (new CSMWorld::CreateCommand (
dynamic_cast<CSMWorld::IdTable&> (*mData.getTableModel (mListId)), id));
configureCreateCommand (*command);
configureCreateCommand (*command);
mUndoStack.push (command.release());
mUndoStack.push (command.release());
emit done();
emit requestFocus (id);
emit done();
emit requestFocus (id);
}
}
}
}
void CSVWorld::GenericCreator::cloneMode(const std::string& originId,
const CSMWorld::UniversalId::Type type)
{
mCloneMode = true;
mClonedId = originId;
mClonedType = type;
}
void CSVWorld::GenericCreator::toggleWidgets(bool active)
{
}

@ -1,6 +1,7 @@
#ifndef CSV_WORLD_GENERICCREATOR_H
#define CSV_WORLD_GENERICCREATOR_H
class QString;
class QPushButton;
class QLineEdit;
class QHBoxLayout;
@ -28,6 +29,11 @@ namespace CSVWorld
std::string mErrors;
QHBoxLayout *mLayout;
bool mLocked;
std::string mClonedId;
CSMWorld::UniversalId::Type mClonedType;
protected:
bool mCloneMode;
protected:
@ -57,11 +63,15 @@ namespace CSVWorld
virtual void reset();
virtual void toggleWidgets (bool active = true);
virtual void cloneMode(const std::string& originId,
const CSMWorld::UniversalId::Type type);
virtual std::string getErrors() const;
///< Return formatted error descriptions for the current state of the creator. if an empty
/// string is returned, there is no error.
private slots:
void textChanged (const QString& text);

@ -40,4 +40,10 @@ void CSVWorld::ReferenceableCreator::reset()
{
mType->setCurrentIndex (0);
GenericCreator::reset();
}
}
void CSVWorld::ReferenceableCreator::toggleWidgets(bool active)
{
CSVWorld::GenericCreator::toggleWidgets(active);
mType->setEnabled(active);
}

@ -23,6 +23,7 @@ namespace CSVWorld
const CSMWorld::UniversalId& id);
virtual void reset();
virtual void toggleWidgets(bool active = true);
};
}

@ -40,15 +40,20 @@ CSVWorld::ReferenceCreator::ReferenceCreator (CSMWorld::Data& data, QUndoStack&
void CSVWorld::ReferenceCreator::reset()
{
GenericCreator::reset();
mCell->setText ("");
mId = getData().getReferences().getNewId();
GenericCreator::reset();
}
std::string CSVWorld::ReferenceCreator::getErrors() const
{
std::string errors = GenericCreator::getErrors();
if (mCloneMode)
{
return errors;
}
std::string cell = mCell->text().toUtf8().constData();
if (cell.empty())
@ -72,4 +77,17 @@ std::string CSVWorld::ReferenceCreator::getErrors() const
void CSVWorld::ReferenceCreator::cellChanged()
{
update();
}
}
void CSVWorld::ReferenceCreator::toggleWidgets(bool active)
{
CSVWorld::GenericCreator::toggleWidgets(active);
mCell->setEnabled(active);
}
void CSVWorld::ReferenceCreator::cloneMode(const std::string& originId,
const CSMWorld::UniversalId::Type type)
{
CSVWorld::GenericCreator::cloneMode(originId, type);
cellChanged(); //otherwise ok button will remain disabled
}

@ -25,7 +25,11 @@ namespace CSVWorld
ReferenceCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id);
virtual void cloneMode(const std::string& originId,
const CSMWorld::UniversalId::Type type);
virtual void reset();
virtual void toggleWidgets(bool active = true);
virtual std::string getErrors() const;
///< Return formatted error descriptions for the current state of the creator. if an empty

@ -28,7 +28,11 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
if (!mEditLock)
{
if (selectedRows.size()==1)
{
menu.addAction (mEditAction);
if (mCreateAction)
menu.addAction(mCloneAction);
}
if (mCreateAction)
menu.addAction (mCreateAction);
@ -155,7 +159,7 @@ std::vector<std::string> CSVWorld::Table::listDeletableSelectedIds() const
CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack,
bool createAndDelete, bool sorting)
: mUndoStack (undoStack), mCreateAction (0), mEditLock (false), mRecordStatusDisplay (0)
: mUndoStack (undoStack), mCreateAction (0), mCloneAction(0), mEditLock (false), mRecordStatusDisplay (0)
{
mModel = &dynamic_cast<CSMWorld::IdTable&> (*data.getTableModel (id));
@ -199,6 +203,10 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q
mCreateAction = new QAction (tr ("Add Record"), this);
connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest()));
addAction (mCreateAction);
mCloneAction = new QAction (tr ("Clone Record"), this);
connect(mCloneAction, SIGNAL (triggered()), this, SLOT (cloneRecord()));
addAction(mCloneAction);
}
mRevertAction = new QAction (tr ("Revert Record"), this);
@ -295,6 +303,19 @@ void CSVWorld::Table::editRecord()
}
}
void CSVWorld::Table::cloneRecord()
{
if (!mEditLock)
{
QModelIndexList selectedRows = selectionModel()->selectedRows();
const CSMWorld::UniversalId& toClone = getUniversalId(selectedRows.begin()->row());
if (selectedRows.size()==1 && !mModel->getRecord(toClone.getId()).isDeleted())
{
emit cloneRequest (toClone);
}
}
}
void CSVWorld::Table::moveUpRecord()
{
QModelIndexList selectedRows = selectionModel()->selectedRows();

@ -32,6 +32,7 @@ namespace CSVWorld
QUndoStack& mUndoStack;
QAction *mEditAction;
QAction *mCreateAction;
QAction *mCloneAction;
QAction *mRevertAction;
QAction *mDeleteAction;
QAction *mMoveUpAction;
@ -73,6 +74,7 @@ namespace CSVWorld
/// \param modified Number of added and modified records
void createRequest();
void cloneRequest(const CSMWorld::UniversalId&);
private slots:
@ -82,6 +84,8 @@ namespace CSVWorld
void editRecord();
void cloneRecord();
void moveUpRecord();
void moveDownRecord();

@ -153,7 +153,19 @@ void CSVWorld::TableBottomBox::tableSizeChanged (int size, int deleted, int modi
void CSVWorld::TableBottomBox::createRequest()
{
mCreator->reset();
mCreator->toggleWidgets(true);
mLayout->setCurrentWidget (mCreator);
setVisible (true);
mCreating = true;
}
}
void CSVWorld::TableBottomBox::cloneRequest(const std::string& id,
const CSMWorld::UniversalId::Type type)
{
mCreator->reset();
mCreator->cloneMode(id, type);
mLayout->setCurrentWidget(mCreator);
mCreator->toggleWidgets(false);
setVisible (true);
mCreating = true;
}

@ -2,6 +2,7 @@
#define CSV_WORLD_BOTTOMBOX_H
#include <QWidget>
#include <apps/opencs/model/world/universalid.hpp>
class QLabel;
class QStackedLayout;
@ -76,6 +77,8 @@ namespace CSVWorld
/// \param modified Number of added and modified records
void createRequest();
void cloneRequest(const std::string& id,
const CSMWorld::UniversalId::Type type);
};
}

@ -6,7 +6,6 @@
#include "../../model/doc/document.hpp"
#include "../filter/filterbox.hpp"
#include "table.hpp"
#include "tablebottombox.hpp"
#include "creator.hpp"
@ -46,8 +45,15 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D
mTable->selectionSizeUpdate();
if (mBottom->canCreateAndDelete())
{
connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest()));
connect (mTable, SIGNAL (cloneRequest(const CSMWorld::UniversalId&)), this,
SLOT(cloneRequest(const CSMWorld::UniversalId&)));
connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)),
mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)));
}
connect (mBottom, SIGNAL (requestFocus (const std::string&)),
mTable, SLOT (requestFocus (const std::string&)));
@ -75,4 +81,9 @@ void CSVWorld::TableSubView::updateEditorSetting(const QString &settingName, con
void CSVWorld::TableSubView::setStatusBar (bool show)
{
mBottom->setStatusBar (show);
}
}
void CSVWorld::TableSubView::cloneRequest(const CSMWorld::UniversalId& toClone)
{
emit cloneRequest(toClone.getId(), toClone.getType());
}

@ -5,6 +5,11 @@
class QModelIndex;
namespace CSMWorld
{
class IdTable;
}
namespace CSMDoc
{
class Document;
@ -34,9 +39,14 @@ namespace CSVWorld
virtual void setStatusBar (bool show);
signals:
void cloneRequest(const std::string&,
const CSMWorld::UniversalId::Type);
private slots:
void editRequest (int row);
void cloneRequest (const CSMWorld::UniversalId& toClone);
};
}

@ -69,12 +69,16 @@ add_openmw_dir (mwmechanics
mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects
drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow
aiescort aiactivate aicombat repair enchanting pathfinding security spellsuccess spellcasting
disease pickpocket levelledlist
disease pickpocket levelledlist combat steering
)
add_openmw_dir (mwstate
statemanagerimp charactermanager character
)
add_openmw_dir (mwbase
environment world scriptmanager dialoguemanager journal soundmanager mechanicsmanager
inputmanager windowmanager
inputmanager windowmanager statemanager
)
# Main executable

@ -7,6 +7,8 @@
#include <MyGUI_WidgetManager.h>
#include <SDL.h>
#include <components/compiler/extensions0.hpp>
#include <components/bsa/bsa_archive.hpp>
@ -41,8 +43,7 @@
#include "mwmechanics/mechanicsmanagerimp.hpp"
#include <SDL.h>
#include "mwstate/statemanagerimp.hpp"
void OMW::Engine::executeLocalScripts()
{
@ -88,31 +89,47 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
if (mUseSound)
MWBase::Environment::get().getSoundManager()->update(frametime);
// global scripts
MWBase::Environment::get().getScriptManager()->getGlobalScripts().run();
bool paused = MWBase::Environment::get().getWindowManager()->isGuiMode();
bool changed = MWBase::Environment::get().getWorld()->hasCellChanged();
// update game state
MWBase::Environment::get().getStateManager()->update (frametime);
// local scripts
executeLocalScripts(); // This does not handle the case where a global script causes a cell
// change, followed by a cell change in a local script during the same
// frame.
if (MWBase::Environment::get().getStateManager()->getState()==
MWBase::StateManager::State_Running)
{
// global scripts
MWBase::Environment::get().getScriptManager()->getGlobalScripts().run();
// passing of time
if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
MWBase::Environment::get().getWorld()->advanceTime(
frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600);
bool changed = MWBase::Environment::get().getWorld()->hasCellChanged();
// local scripts
executeLocalScripts(); // This does not handle the case where a global script causes a
// cell change, followed by a cell change in a local script during
// the same frame.
if (changed) // keep change flag for another frame, if cell changed happened in local script
MWBase::Environment::get().getWorld()->markCellAsUnchanged();
if (!paused)
MWBase::Environment::get().getWorld()->advanceTime(
frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600);
}
if (changed) // keep change flag for another frame, if cell changed happend in local script
MWBase::Environment::get().getWorld()->markCellAsUnchanged();
// update actors
MWBase::Environment::get().getMechanicsManager()->update(frametime,
MWBase::Environment::get().getWindowManager()->isGuiMode());
paused);
if (MWBase::Environment::get().getStateManager()->getState()==
MWBase::StateManager::State_Running)
{
MWWorld::Ptr player = mEnvironment.getWorld()->getPlayerPtr();
if(!paused && player.getClass().getCreatureStats(player).isDead())
MWBase::Environment::get().getStateManager()->endGame();
}
// update world
MWBase::Environment::get().getWorld()->update(frametime, MWBase::Environment::get().getWindowManager()->isGuiMode());
MWBase::Environment::get().getWorld()->update(frametime, paused);
// update GUI
Ogre::RenderWindow* window = mOgre->getWindow();
@ -135,7 +152,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
: mOgre (0)
, mFpsLevel(0)
, mVerboseScripts (false)
, mNewGame (false)
, mSkipMenu (false)
, mUseSound (true)
, mCompileAll (false)
, mScriptContext (0)
@ -265,12 +282,7 @@ void OMW::Engine::setCell (const std::string& cellName)
void OMW::Engine::addContentFile(const std::string& file)
{
if (file.find_last_of(".") == std::string::npos)
{
throw std::runtime_error("Missing extension in content file!");
}
mContentFiles.push_back(file);
mContentFiles.push_back(file);
}
void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity)
@ -278,9 +290,9 @@ void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity)
mVerboseScripts = scriptsVerbosity;
}
void OMW::Engine::setNewGame(bool newGame)
void OMW::Engine::setSkipMenu (bool skipMenu)
{
mNewGame = newGame;
mSkipMenu = skipMenu;
}
std::string OMW::Engine::loadSettings (Settings::Manager & settings)
@ -326,6 +338,9 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings)
void OMW::Engine::prepareEngine (Settings::Manager & settings)
{
mEnvironment.setStateManager (
new MWState::StateManager (mCfgMgr.getUserDataPath() / "saves", mContentFiles.at (0)));
Nif::NIFFile::CacheLock cachelock;
std::string renderSystem = settings.getString("render system", "Video");
@ -392,10 +407,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
input->setPlayer(&mEnvironment.getWorld()->getPlayer());
window->initUI();
if (mNewGame)
// still redundant work here: recreate CharacterCreation(),
// double update visibility etc.
window->setNewGame(true);
window->renderWorldMap();
//Load translation data
@ -403,7 +414,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
for (size_t i = 0; i < mContentFiles.size(); i++)
mTranslationDataStorage.loadTranslationData(mFileCollections, mContentFiles[i]);
Compiler::registerExtensions (mExtensions);
Compiler::registerExtensions (mExtensions);
// Create sound system
mEnvironment.setSoundManager (new MWSound::SoundManager(mUseSound));
@ -427,12 +438,12 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
mechanics->buildPlayer();
window->updatePlayer();
if (!mNewGame)
{
// load cell
ESM::Position pos;
MWBase::World *world = MWBase::Environment::get().getWorld();
// load cell
ESM::Position pos;
MWBase::World *world = MWBase::Environment::get().getWorld();
if (!mCellName.empty())
{
if (world->findExteriorPosition(mCellName, pos)) {
world->changeToExteriorCell (pos);
}
@ -442,7 +453,11 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
}
}
else
mEnvironment.getWorld()->startNewGame();
{
pos.pos[0] = pos.pos[1] = pos.pos[2] = 0;
pos.rot[0] = pos.rot[1] = pos.pos[2] = 0;
world->changeToExteriorCell (pos);
}
Ogre::FrameEvent event;
event.timeSinceLastEvent = 0;
@ -468,7 +483,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
void OMW::Engine::go()
{
assert (!mCellName.empty());
assert (!mContentFiles.empty());
assert (!mOgre);
@ -489,8 +503,14 @@ void OMW::Engine::go()
if (!mStartupScript.empty())
MWBase::Environment::get().getWindowManager()->executeInConsole (mStartupScript);
// start in main menu
if (!mSkipMenu)
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
else
MWBase::Environment::get().getStateManager()->newGame (true);
// Start the main rendering loop
while (!mEnvironment.getRequestExit())
while (!mEnvironment.get().getStateManager()->hasQuitRequest())
Ogre::Root::getSingleton().renderOneFrame();
// Save user settings
@ -509,6 +529,9 @@ void OMW::Engine::activate()
if (ptr.isEmpty())
return;
if (ptr.getClass().getName(ptr) == "") // objects without name presented to user can never be activated
return;
MWScript::InterpreterContext interpreterContext (&ptr.getRefData().getLocals(), ptr);
boost::shared_ptr<MWWorld::Action> action =

@ -71,7 +71,7 @@ namespace OMW
std::vector<std::string> mContentFiles;
int mFpsLevel;
bool mVerboseScripts;
bool mNewGame;
bool mSkipMenu;
bool mUseSound;
bool mCompileAll;
std::string mFocusName;
@ -151,8 +151,7 @@ namespace OMW
/// Disable or enable all sounds
void setSoundUsage(bool soundUsage);
/// Start as a new game.
void setNewGame(bool newGame);
void setSkipMenu (bool skipMenu);
void setGrabMouse(bool grab) { mGrab = grab; }

@ -115,7 +115,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
("resources", bpo::value<std::string>()->default_value("resources"),
"set resources directory")
("start", bpo::value<std::string>()->default_value("Beshara"),
("start", bpo::value<std::string>()->default_value(""),
"set initial cell")
("content", bpo::value<StringsVector>()->default_value(StringsVector(), "")
@ -136,8 +136,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
("script-run", bpo::value<std::string>()->default_value(""),
"select a file containing a list of console commands that is executed on startup")
("new-game", bpo::value<bool>()->implicit_value(true)
->default_value(false), "activate char gen/new game mechanics")
("skip-menu", bpo::value<bool>()->implicit_value(true)
->default_value(false), "skip main menu on game startup")
("fs-strict", bpo::value<bool>()->implicit_value(true)
->default_value(false), "strict file system handling (no case folding)")
@ -231,7 +231,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
// startup-settings
engine.setCell(variables["start"].as<std::string>());
engine.setNewGame(variables["new-game"].as<bool>());
engine.setSkipMenu (variables["skip-menu"].as<bool>());
// other settings
engine.setSoundUsage(!variables["no-sound"].as<bool>());

@ -11,13 +11,14 @@
#include "mechanicsmanager.hpp"
#include "inputmanager.hpp"
#include "windowmanager.hpp"
#include "statemanager.hpp"
MWBase::Environment *MWBase::Environment::sThis = 0;
bool MWBase::Environment::sExit = false;
MWBase::Environment::Environment()
: mWorld (0), mSoundManager (0), mScriptManager (0), mWindowManager (0),
mMechanicsManager (0), mDialogueManager (0), mJournal (0), mInputManager (0), mFrameDuration (0)
mMechanicsManager (0), mDialogueManager (0), mJournal (0), mInputManager (0), mFrameDuration (0),
mStateManager (0)
{
assert (!sThis);
sThis = this;
@ -69,6 +70,11 @@ void MWBase::Environment::setInputManager (InputManager *inputManager)
mInputManager = inputManager;
}
void MWBase::Environment::setStateManager (StateManager *stateManager)
{
mStateManager = stateManager;
}
void MWBase::Environment::setFrameDuration (float duration)
{
mFrameDuration = duration;
@ -122,6 +128,12 @@ MWBase::InputManager *MWBase::Environment::getInputManager() const
return mInputManager;
}
MWBase::StateManager *MWBase::Environment::getStateManager() const
{
assert (mStateManager);
return mStateManager;
}
float MWBase::Environment::getFrameDuration() const
{
return mFrameDuration;
@ -152,6 +164,9 @@ void MWBase::Environment::cleanup()
delete mInputManager;
mInputManager = 0;
delete mStateManager;
mStateManager = 0;
}
const MWBase::Environment& MWBase::Environment::get()

@ -11,6 +11,7 @@ namespace MWBase
class MechanicsManager;
class InputManager;
class WindowManager;
class StateManager;
/// \brief Central hub for mw-subsystems
///
@ -30,10 +31,9 @@ namespace MWBase
DialogueManager *mDialogueManager;
Journal *mJournal;
InputManager *mInputManager;
StateManager *mStateManager;
float mFrameDuration;
static bool sExit;
Environment (const Environment&);
///< not implemented
@ -46,9 +46,6 @@ namespace MWBase
~Environment();
static void setRequestExit () { sExit = true; }
static bool getRequestExit () { return sExit; }
void setWorld (World *world);
void setSoundManager (SoundManager *soundManager);
@ -65,6 +62,8 @@ namespace MWBase
void setInputManager (InputManager *inputManager);
void setStateManager (StateManager *stateManager);
void setFrameDuration (float duration);
///< Set length of current frame in seconds.
@ -84,6 +83,8 @@ namespace MWBase
InputManager *getInputManager() const;
StateManager *getStateManager() const;
float getFrameDuration() const;
void cleanup();

@ -5,10 +5,18 @@
#include <deque>
#include <map>
#include <libs/platform/stdint.h>
#include "../mwdialogue/journalentry.hpp"
#include "../mwdialogue/topic.hpp"
#include "../mwdialogue/quest.hpp"
namespace ESM
{
class ESMReader;
class ESMWriter;
}
namespace MWBase
{
/// \brief Interface for the player's journal (implemented in MWDialogue)
@ -46,7 +54,7 @@ namespace MWBase
virtual int getJournalIndex (const std::string& id) const = 0;
///< Get the journal index.
virtual void addTopic (const std::string& topicId, const std::string& infoId) = 0;
virtual void addTopic (const std::string& topicId, const std::string& infoId, const std::string& actorName) = 0;
virtual TEntryIter begin() const = 0;
///< Iterator pointing to the begin of the main journal.
@ -69,6 +77,12 @@ namespace MWBase
virtual TTopicIter topicEnd() const = 0;
///< Iterator pointing past the last topic.
virtual int countSavedGameRecords() const = 0;
virtual void write (ESM::ESMWriter& writer) const = 0;
virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0;
};
}

@ -3,6 +3,7 @@
#include <string>
#include <vector>
#include <list>
namespace Ogre
{
@ -135,28 +136,35 @@ namespace MWBase
float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) = 0;
///< Perform a persuasion action on NPC
virtual void forceStateUpdate(const MWWorld::Ptr &ptr) = 0;
///< Forces an object to refresh its animation state.
virtual void forceStateUpdate(const MWWorld::Ptr &ptr) = 0;
///< Forces an object to refresh its animation state.
virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number=1) = 0;
///< Run animation for a MW-reference. Calls to this function for references that are currently not
/// in the scene should be ignored.
///
/// \param mode 0 normal, 1 immediate start, 2 immediate loop
/// \param count How many times the animation should be run
virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number=1) = 0;
///< Run animation for a MW-reference. Calls to this function for references that are currently not
/// in the scene should be ignored.
///
/// \param mode 0 normal, 1 immediate start, 2 immediate loop
/// \param count How many times the animation should be run
virtual void skipAnimation(const MWWorld::Ptr& ptr) = 0;
///< Skip the animation for the given MW-reference for one frame. Calls to this function for
/// references that are currently not in the scene should be ignored.
virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) = 0;
/// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently
/// paused we may want to do it manually (after equipping permanent enchantment)
virtual void updateMagicEffects (const MWWorld::Ptr& ptr) = 0;
virtual void skipAnimation(const MWWorld::Ptr& ptr) = 0;
///< Skip the animation for the given MW-reference for one frame. Calls to this function for
/// references that are currently not in the scene should be ignored.
virtual void toggleAI() = 0;
virtual bool isAIActive() = 0;
virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) = 0;
virtual void getObjectsInRange (const Ogre::Vector3& position, float radius, std::vector<MWWorld::Ptr>& objects) = 0;
/// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently
/// paused we may want to do it manually (after equipping permanent enchantment)
virtual void updateMagicEffects (const MWWorld::Ptr& ptr) = 0;
///return the list of actors which are following the given actor (ie AiFollow is active and the target is the actor)
virtual std::list<MWWorld::Ptr> getActorsFollowing(const MWWorld::Ptr& actor) = 0;
virtual void toggleAI() = 0;
virtual bool isAIActive() = 0;
virtual void playerLoaded() = 0;
};
}

@ -35,8 +35,6 @@ namespace MWBase
virtual ~ScriptManager() {}
virtual void resetGlobalScripts() = 0;
virtual void run (const std::string& name, Interpreter::Context& interpreterContext) = 0;
///< Run the script with the given name (compile first, if not compiled yet)

@ -147,6 +147,10 @@ namespace MWBase
virtual void update(float duration) = 0;
virtual void setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir, const Ogre::Vector3 &up) = 0;
virtual void updatePtr (const MWWorld::Ptr& old, const MWWorld::Ptr& updated) = 0;
virtual void clear() = 0;
};
}

@ -0,0 +1,81 @@
#ifndef GAME_MWSTATE_STATEMANAGER_H
#define GAME_MWSTATE_STATEMANAGER_H
#include <vector>
#include <string>
namespace MWState
{
struct Slot;
class Character;
}
namespace MWBase
{
/// \brief Interface for game state manager (implemented in MWState)
class StateManager
{
public:
enum State
{
State_NoGame,
State_Ended,
State_Running
};
typedef std::vector<MWState::Character>::const_iterator CharacterIterator;
private:
StateManager (const StateManager&);
///< not implemented
StateManager& operator= (const StateManager&);
///< not implemented
public:
StateManager() {}
virtual ~StateManager() {}
virtual void requestQuit() = 0;
virtual bool hasQuitRequest() const = 0;
virtual void askLoadRecent() = 0;
virtual State getState() const = 0;
virtual void newGame (bool bypass = false) = 0;
///< Start a new game.
///
/// \param bypass Skip new game mechanics.
virtual void endGame() = 0;
virtual void saveGame (const std::string& description, const MWState::Slot *slot = 0) = 0;
///< Write a saved game to \a slot or create a new slot if \a slot == 0.
///
/// \note Slot must belong to the current character.
virtual void loadGame (const MWState::Character *character, const MWState::Slot *slot) = 0;
///< Load a saved game file from \a slot.
///
/// \note \a slot must belong to \a character.
virtual MWState::Character *getCurrentCharacter (bool create = true) = 0;
///< \param create Create a new character, if there is no current character.
virtual CharacterIterator characterBegin() = 0;
///< Any call to SaveGame and getCurrentCharacter can invalidate the returned
/// iterator.
virtual CharacterIterator characterEnd() = 0;
virtual void update (float duration) = 0;
};
}
#endif

@ -33,6 +33,8 @@ namespace OEngine
namespace ESM
{
struct Class;
class ESMReader;
class ESMWriter;
}
namespace MWWorld
@ -279,12 +281,19 @@ namespace MWBase
virtual const Translation::Storage& getTranslationDataStorage() const = 0;
/// Warning: do not use MyGUI::InputManager::setKeyFocusWidget directly. Instead use this.
virtual void setKeyFocusWidget (MyGUI::Widget* widget) = 0;
virtual Loading::Listener* getLoadingScreen() = 0;
/// Should the cursor be visible?
virtual bool getCursorVisible() = 0;
/// Clear all savegame-specific data
virtual void clear() = 0;
virtual void write (ESM::ESMWriter& writer) = 0;
virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0;
};
}

@ -2,6 +2,7 @@
#define GAME_MWBASE_WORLD_H
#include <vector>
#include <map>
#include <components/settings/settings.hpp>
@ -30,12 +31,14 @@ namespace OEngine
namespace ESM
{
class ESMReader;
class ESMWriter;
struct Position;
struct Cell;
struct Class;
struct Potion;
struct Spell;
struct NPC;
struct CellId;
}
namespace MWRender
@ -94,13 +97,26 @@ namespace MWBase
virtual void startNewGame() = 0;
virtual void clear() = 0;
virtual int countSavedGameRecords() const = 0;
virtual void write (ESM::ESMWriter& writer) const = 0;
virtual void readRecord (ESM::ESMReader& reader, int32_t type,
const std::map<int, int>& contentFileMap) = 0;
virtual OEngine::Render::Fader* getFader() = 0;
///< \ŧodo remove this function. Rendering details should not be exposed.
///< \todo remove this function. Rendering details should not be exposed.
virtual MWWorld::CellStore *getExterior (int x, int y) = 0;
virtual MWWorld::CellStore *getInterior (const std::string& name) = 0;
virtual MWWorld::CellStore *getCell (const ESM::CellId& id) = 0;
virtual void useDeathCamera() = 0;
virtual void setWaterHeight(const float height) = 0;
virtual void toggleWater() = 0;
@ -139,16 +155,26 @@ namespace MWBase
virtual bool isPositionExplored (float nX, float nY, int x, int y, bool interior) = 0;
///< see MWRender::LocalMap::isPositionExplored
virtual MWWorld::Globals::Data& getGlobalVariable (const std::string& name) = 0;
virtual void setGlobalInt (const std::string& name, int value) = 0;
///< Set value independently from real type.
virtual void setGlobalFloat (const std::string& name, float value) = 0;
///< Set value independently from real type.
virtual MWWorld::Globals::Data getGlobalVariable (const std::string& name) const = 0;
virtual int getGlobalInt (const std::string& name) const = 0;
///< Get value independently from real type.
virtual float getGlobalFloat (const std::string& name) const = 0;
///< Get value independently from real type.
virtual char getGlobalVariableType (const std::string& name) const = 0;
///< Return ' ', if there is no global variable with this name.
virtual std::vector<std::string> getGlobals () const = 0;
virtual std::string getCurrentCellName() const = 0;
virtual std::string getCellName (const MWWorld::CellStore *cell = 0) const = 0;
///< Return name of the cell.
///
/// \note If cell==0, the cell the player is currently in will be used instead to
/// generate a name.
virtual void removeRefScript (MWWorld::RefData *ref) = 0;
//< Remove the script attached to ref from mLocalScripts
@ -185,8 +211,12 @@ namespace MWBase
virtual void setDay (int day) = 0;
///< Set in-game time day.
virtual int getDay() = 0;
virtual int getMonth() = 0;
virtual int getDay() const = 0;
virtual int getMonth() const = 0;
virtual int getYear() const = 0;
virtual std::string getMonthName (int month = -1) const = 0;
///< Return name of month (-1: current month)
virtual MWWorld::TimeStamp getTimeStamp() const = 0;
///< Return current in-game time stamp.
@ -215,6 +245,8 @@ namespace MWBase
virtual void changeToExteriorCell (const ESM::Position& position) = 0;
///< Move to exterior cell.
virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position) = 0;
virtual const ESM::Cell *getExterior (const std::string& cellName) const = 0;
///< Return a cell matching the given name or a 0-pointer, if there is no such cell.
@ -225,7 +257,7 @@ namespace MWBase
/// Returns a pointer to the object the provided object would hit (if within the
/// specified distance), and the point where the hit occurs. This will attempt to
/// use the "Head" node as a basis.
/// use the "Head" node, or alternatively the "Bip01 Head" node as a basis.
virtual std::pair<MWWorld::Ptr,Ogre::Vector3> getHitContact(const MWWorld::Ptr &ptr, float distance) = 0;
virtual void adjustPosition (const MWWorld::Ptr& ptr) = 0;
@ -336,7 +368,7 @@ namespace MWBase
virtual bool isSwimming(const MWWorld::Ptr &object) const = 0;
///Is the head of the creature underwater?
virtual bool isSubmerged(const MWWorld::Ptr &object) const = 0;
virtual bool isUnderwater(const MWWorld::Ptr::CellStore* cell, const Ogre::Vector3 &pos) const = 0;
virtual bool isUnderwater(const MWWorld::CellStore* cell, const Ogre::Vector3 &pos) const = 0;
virtual bool isOnGround(const MWWorld::Ptr &ptr) const = 0;
virtual void togglePOV() = 0;
@ -384,6 +416,7 @@ namespace MWBase
virtual void playVideo(const std::string& name, bool allowSkipping) = 0;
virtual void stopVideo() = 0;
virtual void frameStarted (float dt, bool paused) = 0;
virtual void screenshot (Ogre::Image& image, int w, int h) = 0;
/// Find default position inside exterior cell specified by name
/// \return false if exterior with given name not exists, true otherwise
@ -428,6 +461,8 @@ namespace MWBase
virtual void launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects,
const MWWorld::Ptr& actor, const std::string& sourceName) = 0;
virtual const std::vector<std::string>& getContentFiles() const = 0;
virtual void breakInvisibility (const MWWorld::Ptr& actor) = 0;
// Are we in an exterior or pseudo-exterior cell and it's night?
@ -466,6 +501,9 @@ namespace MWBase
/// Spawn a blood effect for \a ptr at \a worldPosition
virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition) = 0;
virtual void explodeSpell (const Ogre::Vector3& origin, const MWWorld::Ptr& object, const ESM::EffectList& effects,
const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName) = 0;
};
}

@ -289,7 +289,7 @@ namespace MWClass
std::pair<int, std::string> Armor::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const
{
MWWorld::InventoryStore& invStore = MWWorld::Class::get(npc).getInventoryStore(npc);
MWWorld::InventoryStore& invStore = npc.getClass().getInventoryStore(npc);
if (ptr.getCellRef().mCharge == 0)
return std::make_pair(0, "#{sInventoryMessage1}");
@ -300,20 +300,23 @@ namespace MWClass
if (slots_.first.empty())
return std::make_pair(0, "");
std::string npcRace = npc.get<ESM::NPC>()->mBase->mRace;
// Beast races cannot equip shoes / boots, or full helms (head part vs hair part)
const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(npcRace);
if(race->mData.mFlags & ESM::Race::Beast)
if (npc.getClass().isNpc())
{
std::vector<ESM::PartReference> parts = ptr.get<ESM::Armor>()->mBase->mParts.mParts;
std::string npcRace = npc.get<ESM::NPC>()->mBase->mRace;
for(std::vector<ESM::PartReference>::iterator itr = parts.begin(); itr != parts.end(); ++itr)
// Beast races cannot equip shoes / boots, or full helms (head part vs hair part)
const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(npcRace);
if(race->mData.mFlags & ESM::Race::Beast)
{
if((*itr).mPart == ESM::PRT_Head)
return std::make_pair(0, "#{sNotifyMessage13}");
if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot)
return std::make_pair(0, "#{sNotifyMessage14}");
std::vector<ESM::PartReference> parts = ptr.get<ESM::Armor>()->mBase->mParts.mParts;
for(std::vector<ESM::PartReference>::iterator itr = parts.begin(); itr != parts.end(); ++itr)
{
if((*itr).mPart == ESM::PRT_Head)
return std::make_pair(0, "#{sNotifyMessage13}");
if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot)
return std::make_pair(0, "#{sNotifyMessage14}");
}
}
}
@ -363,12 +366,12 @@ namespace MWClass
return MWWorld::Ptr(&cell.mArmors.insert(*ref), &cell);
}
float Armor::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
int Armor::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
{
MWWorld::LiveCellRef<ESM::Armor> *ref =
ptr.get<ESM::Armor>();
return ref->mBase->mData.mEnchant/10.f;
return ref->mBase->mData.mEnchant;
}
bool Armor::canSell (const MWWorld::Ptr& item, int npcServices) const

@ -79,7 +79,7 @@ namespace MWClass
virtual std::string getModel(const MWWorld::Ptr &ptr) const;
virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
virtual int getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const;
};

@ -189,12 +189,12 @@ namespace MWClass
return MWWorld::Ptr(&cell.mBooks.insert(*ref), &cell);
}
float Book::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
int Book::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
{
MWWorld::LiveCellRef<ESM::Book> *ref =
ptr.get<ESM::Book>();
return ref->mBase->mData.mEnchant/10.f;
return ref->mBase->mData.mEnchant;
}
bool Book::canSell (const MWWorld::Ptr& item, int npcServices) const

@ -58,7 +58,7 @@ namespace MWClass
virtual std::string getModel(const MWWorld::Ptr &ptr) const;
virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
virtual int getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
virtual float getWeight (const MWWorld::Ptr& ptr) const;

@ -238,20 +238,23 @@ namespace MWClass
if (slots_.first.empty())
return std::make_pair(0, "");
std::string npcRace = npc.get<ESM::NPC>()->mBase->mRace;
// Beast races cannot equip shoes / boots, or full helms (head part vs hair part)
const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(npcRace);
if(race->mData.mFlags & ESM::Race::Beast)
if (npc.getClass().isNpc())
{
std::vector<ESM::PartReference> parts = ptr.get<ESM::Clothing>()->mBase->mParts.mParts;
std::string npcRace = npc.get<ESM::NPC>()->mBase->mRace;
for(std::vector<ESM::PartReference>::iterator itr = parts.begin(); itr != parts.end(); ++itr)
// Beast races cannot equip shoes / boots, or full helms (head part vs hair part)
const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(npcRace);
if(race->mData.mFlags & ESM::Race::Beast)
{
if((*itr).mPart == ESM::PRT_Head)
return std::make_pair(0, "#{sNotifyMessage13}");
if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot)
return std::make_pair(0, "#{sNotifyMessage15}");
std::vector<ESM::PartReference> parts = ptr.get<ESM::Clothing>()->mBase->mParts.mParts;
for(std::vector<ESM::PartReference>::iterator itr = parts.begin(); itr != parts.end(); ++itr)
{
if((*itr).mPart == ESM::PRT_Head)
return std::make_pair(0, "#{sNotifyMessage13}");
if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot)
return std::make_pair(0, "#{sNotifyMessage15}");
}
}
}
@ -276,12 +279,12 @@ namespace MWClass
return MWWorld::Ptr(&cell.mClothes.insert(*ref), &cell);
}
float Clothing::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
int Clothing::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
{
MWWorld::LiveCellRef<ESM::Clothing> *ref =
ptr.get<ESM::Clothing>();
return ref->mBase->mData.mEnchant/10.f;
return ref->mBase->mData.mEnchant;
}
bool Clothing::canSell (const MWWorld::Ptr& item, int npcServices) const

@ -71,7 +71,7 @@ namespace MWClass
virtual std::string getModel(const MWWorld::Ptr &ptr) const;
virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
virtual int getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
virtual float getWeight (const MWWorld::Ptr& ptr) const;

@ -2,6 +2,7 @@
#include "container.hpp"
#include <components/esm/loadcont.hpp>
#include <components/esm/containerstate.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -258,4 +259,26 @@ namespace MWClass
return MWWorld::Ptr(&cell.mContainers.insert(*ref), &cell);
}
void Container::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const
{
const ESM::ContainerState& state2 = dynamic_cast<const ESM::ContainerState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore.
readState (state2.mInventory);
}
void Container::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const
{
ESM::ContainerState& state2 = dynamic_cast<ESM::ContainerState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore.
writeState (state2.mInventory);
}
}

@ -54,6 +54,14 @@ namespace MWClass
virtual void unlock (const MWWorld::Ptr& ptr) const;
///< Unlock object
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const;
///< Read additional state from \a state into \a ptr.
virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const;
///< Write additional state from \a ptr into \a state.
static void registerSelf();
virtual std::string getModel(const MWWorld::Ptr &ptr) const;

@ -2,10 +2,12 @@
#include "creature.hpp"
#include <components/esm/loadcrea.hpp>
#include <components/esm/creaturestate.hpp>
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/magiceffects.hpp"
#include "../mwmechanics/movement.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
@ -26,22 +28,30 @@
#include "../mwgui/tooltips.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwmechanics/npcstats.hpp"
#include "../mwmechanics/combat.hpp"
namespace
{
struct CustomData : public MWWorld::CustomData
{
MWMechanics::CreatureStats mCreatureStats;
MWWorld::ContainerStore mContainerStore;
MWWorld::ContainerStore* mContainerStore; // may be InventoryStore for some creatures
MWMechanics::Movement mMovement;
virtual MWWorld::CustomData *clone() const;
CustomData() : mContainerStore(0) {}
virtual ~CustomData() { delete mContainerStore; }
};
MWWorld::CustomData *CustomData::clone() const
{
return new CustomData (*this);
CustomData* cloned = new CustomData (*this);
cloned->mContainerStore = mContainerStore->clone();
return cloned;
}
}
@ -106,15 +116,23 @@ namespace MWClass
data->mCreatureStats.getSpells().add (*iter);
// inventory
data->mContainerStore.fill(ref->mBase->mInventory, getId(ptr), "",
if (ref->mBase->mFlags & ESM::Creature::Weapon)
data->mContainerStore = new MWWorld::InventoryStore();
else
data->mContainerStore = new MWWorld::ContainerStore();
// store
ptr.getRefData().setCustomData (data.release());
getContainerStore(ptr).fill(ref->mBase->mInventory, getId(ptr), "",
MWBase::Environment::get().getWorld()->getStore());
// TODO: this is not quite correct, in vanilla the merchant's gold pool is not available in his inventory.
// (except for gold you gave him)
data->mContainerStore.add(MWWorld::ContainerStore::sGoldId, ref->mBase->mData.mGold, ptr);
getContainerStore(ptr).add(MWWorld::ContainerStore::sGoldId, ref->mBase->mData.mGold, ptr);
// store
ptr.getRefData().setCustomData (data.release());
if (ref->mBase->mFlags & ESM::Creature::Weapon)
getInventoryStore(ptr).autoEquip(ptr);
}
}
@ -133,8 +151,10 @@ namespace MWClass
void Creature::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
{
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
MWRender::Actors& actors = renderingInterface.getActors();
actors.insertCreature(ptr);
actors.insertCreature(ptr, ref->mBase->mFlags & ESM::Creature::Weapon);
}
void Creature::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const
@ -178,9 +198,40 @@ namespace MWClass
{
MWWorld::LiveCellRef<ESM::Creature> *ref =
ptr.get<ESM::Creature>();
const MWWorld::Store<ESM::GameSetting> &gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
MWMechanics::CreatureStats &stats = getCreatureStats(ptr);
// Get the weapon used (if hand-to-hand, weapon = inv.end())
MWWorld::Ptr weapon;
if (ptr.getClass().hasInventoryStore(ptr))
{
MWWorld::InventoryStore &inv = getInventoryStore(ptr);
MWWorld::ContainerStoreIterator weaponslot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if (weaponslot != inv.end() && weaponslot->getTypeName() == typeid(ESM::Weapon).name())
weapon = *weaponslot;
}
// Reduce fatigue
// somewhat of a guess, but using the weapon weight makes sense
const float fFatigueAttackBase = gmst.find("fFatigueAttackBase")->getFloat();
const float fFatigueAttackMult = gmst.find("fFatigueAttackMult")->getFloat();
const float fWeaponFatigueMult = gmst.find("fWeaponFatigueMult")->getFloat();
MWMechanics::DynamicStat<float> fatigue = stats.getFatigue();
const float normalizedEncumbrance = getEncumbrance(ptr) / getCapacity(ptr);
float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult;
if (!weapon.isEmpty())
fatigueLoss += weapon.getClass().getWeight(weapon) * stats.getAttackStrength() * fWeaponFatigueMult;
fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss);
stats.setFatigue(fatigue);
// TODO: where is the distance defined?
std::pair<MWWorld::Ptr, Ogre::Vector3> result = MWBase::Environment::get().getWorld()->getHitContact(ptr, 100);
float dist = 200.f;
if (!weapon.isEmpty())
{
const float fCombatDistance = gmst.find("fCombatDistance")->getFloat();
dist = fCombatDistance * weapon.get<ESM::Weapon>()->mBase->mData.mReach;
}
std::pair<MWWorld::Ptr, Ogre::Vector3> result = MWBase::Environment::get().getWorld()->getHitContact(ptr, dist);
if (result.first.isEmpty())
return; // Didn't hit anything
@ -191,7 +242,6 @@ namespace MWClass
Ogre::Vector3 hitPosition = result.second;
MWMechanics::CreatureStats &stats = getCreatureStats(ptr);
MWMechanics::CreatureStats &otherstats = victim.getClass().getCreatureStats(victim);
const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects();
float hitchance = ref->mBase->mData.mCombat +
@ -226,12 +276,64 @@ namespace MWClass
break;
}
// I think this should be random, since attack1-3 animations don't have an attack strength like NPCs do
float damage = min + (max - min) * ::rand()/(RAND_MAX+1.0);
// TODO: do not do this if the attack is blocked
MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition);
if (!weapon.isEmpty())
{
const bool weaphashealth = get(weapon).hasItemHealth(weapon);
const unsigned char *attack = NULL;
if(type == ESM::Weapon::AT_Chop)
attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;
else if(type == ESM::Weapon::AT_Slash)
attack = weapon.get<ESM::Weapon>()->mBase->mData.mSlash;
else if(type == ESM::Weapon::AT_Thrust)
attack = weapon.get<ESM::Weapon>()->mBase->mData.mThrust;
if(attack)
{
float weaponDamage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength());
weaponDamage *= 0.5f + (stats.getAttribute(ESM::Attribute::Luck).getModified() / 100.0f);
if(weaphashealth)
{
int weapmaxhealth = weapon.get<ESM::Weapon>()->mBase->mData.mHealth;
if(weapon.getCellRef().mCharge == -1)
weapon.getCellRef().mCharge = weapmaxhealth;
weaponDamage *= float(weapon.getCellRef().mCharge) / weapmaxhealth;
}
if (!MWBase::Environment::get().getWorld()->getGodModeState())
weapon.getCellRef().mCharge -= std::min(std::max(1,
(int)(damage * gmst.find("fWeaponDamageMult")->getFloat())), weapon.getCellRef().mCharge);
// Weapon broken? unequip it
if (weapon.getCellRef().mCharge == 0)
weapon = *getInventoryStore(ptr).unequipItem(weapon, ptr);
damage += weaponDamage;
}
// Apply "On hit" enchanted weapons
std::string enchantmentName = !weapon.isEmpty() ? weapon.getClass().getEnchantment(weapon) : "";
if (!enchantmentName.empty())
{
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(
enchantmentName);
if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
{
MWMechanics::CastSpell cast(ptr, victim);
cast.mHitPosition = hitPosition;
cast.cast(weapon);
}
}
}
if (!weapon.isEmpty() && MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage))
damage = 0;
if (damage > 0)
MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition);
victim.getClass().onHit(victim, damage, true, MWWorld::Ptr(), ptr, true);
victim.getClass().onHit(victim, damage, true, weapon, ptr, true);
}
void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const
@ -258,31 +360,60 @@ namespace MWClass
ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1);
}
// Check for knockdown
float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * fKnockDownMult->getFloat();
float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified()
* iKnockDownOddsMult->getInt() * 0.01 + iKnockDownOddsBase->getInt();
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
if (ishealth && agilityTerm <= damage && knockdownTerm <= roll)
if (damage > 0.0f && !object.isEmpty())
MWMechanics::resistNormalWeapon(ptr, attacker, object, damage);
if (damage > 0.f)
{
getCreatureStats(ptr).setKnockedDown(true);
// Check for knockdown
float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * fKnockDownMult->getFloat();
float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified()
* iKnockDownOddsMult->getInt() * 0.01 + iKnockDownOddsBase->getInt();
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
if (ishealth && agilityTerm <= damage && knockdownTerm <= roll)
{
getCreatureStats(ptr).setKnockedDown(true);
}
else
getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur?
}
else
getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur?
if(ishealth)
{
if(damage > 0.0f)
if(ishealth)
{
MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "Health Damage", 1.0f, 1.0f);
float health = getCreatureStats(ptr).getHealth().getCurrent() - damage;
setActorHealth(ptr, health, attacker);
float health = getCreatureStats(ptr).getHealth().getCurrent() - damage;
setActorHealth(ptr, health, attacker);
}
else
{
MWMechanics::DynamicStat<float> fatigue(getCreatureStats(ptr).getFatigue());
fatigue.setCurrent(fatigue.getCurrent() - damage, true);
getCreatureStats(ptr).setFatigue(fatigue);
}
}
else
}
void Creature::block(const MWWorld::Ptr &ptr) const
{
MWWorld::InventoryStore& inv = getInventoryStore(ptr);
MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
if (shield == inv.end())
return;
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
switch(shield->getClass().getEquipmentSkill(*shield))
{
MWMechanics::DynamicStat<float> fatigue(getCreatureStats(ptr).getFatigue());
fatigue.setCurrent(fatigue.getCurrent() - damage, true);
getCreatureStats(ptr).setFatigue(fatigue);
case ESM::Skill::LightArmor:
sndMgr->playSound3D(ptr, "Light Armor Hit", 1.0f, 1.0f);
break;
case ESM::Skill::MediumArmor:
sndMgr->playSound3D(ptr, "Medium Armor Hit", 1.0f, 1.0f);
break;
case ESM::Skill::HeavyArmor:
sndMgr->playSound3D(ptr, "Heavy Armor Hit", 1.0f, 1.0f);
break;
default:
return;
}
}
@ -325,18 +456,33 @@ namespace MWClass
return boost::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr));
}
MWWorld::ContainerStore& Creature::getContainerStore (const MWWorld::Ptr& ptr)
const
MWWorld::ContainerStore& Creature::getContainerStore (const MWWorld::Ptr& ptr) const
{
ensureCustomData (ptr);
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore;
return *dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore;
}
MWWorld::InventoryStore& Creature::getInventoryStore(const MWWorld::Ptr &ptr) const
{
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
if (ref->mBase->mFlags & ESM::Creature::Weapon)
return dynamic_cast<MWWorld::InventoryStore&>(getContainerStore(ptr));
else
throw std::runtime_error("this creature has no inventory store");
}
bool Creature::hasInventoryStore(const MWWorld::Ptr &ptr) const
{
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
return (ref->mBase->mFlags & ESM::Creature::Weapon);
}
std::string Creature::getScript (const MWWorld::Ptr& ptr) const
{
MWWorld::LiveCellRef<ESM::Creature> *ref =
ptr.get<ESM::Creature>();
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
return ref->mBase->mScript;
}
@ -613,6 +759,28 @@ namespace MWClass
return 0;
}
void Creature::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const
{
const ESM::CreatureState& state2 = dynamic_cast<const ESM::CreatureState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore->
readState (state2.mInventory);
}
void Creature::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const
{
ESM::CreatureState& state2 = dynamic_cast<ESM::CreatureState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore->
writeState (state2.mInventory);
}
const ESM::GameSetting* Creature::fMinWalkSpeedCreature;
const ESM::GameSetting* Creature::fMaxWalkSpeedCreature;
const ESM::GameSetting *Creature::fEncumberedMoveEffect;

@ -56,6 +56,8 @@ namespace MWClass
virtual void hit(const MWWorld::Ptr& ptr, int type) const;
virtual void block(const MWWorld::Ptr &ptr) const;
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const;
virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const;
@ -68,6 +70,11 @@ namespace MWClass
const MWWorld::Ptr& ptr) const;
///< Return container store
virtual MWWorld::InventoryStore& getInventoryStore (const MWWorld::Ptr& ptr) const;
///< Return inventory store
virtual bool hasInventoryStore (const MWWorld::Ptr &ptr) const;
virtual std::string getScript (const MWWorld::Ptr& ptr) const;
///< Return name of the script attached to ptr
@ -84,7 +91,7 @@ namespace MWClass
virtual bool isEssential (const MWWorld::Ptr& ptr) const;
///< Is \a ptr essential? (i.e. may losing \a ptr make the game unwinnable)
virtual int getServices (const MWWorld::Ptr& actor) const;
virtual bool isPersistent (const MWWorld::Ptr& ptr) const;
@ -118,6 +125,14 @@ namespace MWClass
/// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini)
virtual int getBloodTexture (const MWWorld::Ptr& ptr) const;
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const;
///< Read additional state from \a state into \a ptr.
virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const;
///< Write additional state from \a ptr into \a state.
};
}

@ -3,6 +3,24 @@
#include <components/esm/loadlevlist.hpp>
#include "../mwmechanics/levelledlist.hpp"
#include "../mwworld/customdata.hpp"
namespace
{
struct CustomData : public MWWorld::CustomData
{
// TODO: save the creature we spawned here
virtual MWWorld::CustomData *clone() const;
};
MWWorld::CustomData *CustomData::clone() const
{
return new CustomData (*this);
}
}
namespace MWClass
{
std::string CreatureLevList::getName (const MWWorld::Ptr& ptr) const
@ -16,4 +34,33 @@ namespace MWClass
registerClass (typeid (ESM::CreatureLevList).name(), instance);
}
void CreatureLevList::insertObjectRendering(const MWWorld::Ptr &ptr, MWRender::RenderingInterface &renderingInterface) const
{
ensureCustomData(ptr);
}
void CreatureLevList::ensureCustomData(const MWWorld::Ptr &ptr) const
{
if (!ptr.getRefData().getCustomData())
{
std::auto_ptr<CustomData> data (new CustomData);
MWWorld::LiveCellRef<ESM::CreatureLevList> *ref =
ptr.get<ESM::CreatureLevList>();
std::string id = MWMechanics::getLevelledItem(ref->mBase, true);
if (!id.empty())
{
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
MWWorld::ManualRef ref(store, id);
ref.getPtr().getCellRef().mPos = ptr.getCellRef().mPos;
// TODO: hold on to this for respawn purposes later
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), *ptr.getCell() , ptr.getCellRef().mPos);
}
ptr.getRefData().setCustomData(data.release());
}
}
}

@ -7,6 +7,8 @@ namespace MWClass
{
class CreatureLevList : public MWWorld::Class
{
void ensureCustomData (const MWWorld::Ptr& ptr) const;
public:
virtual std::string getName (const MWWorld::Ptr& ptr) const;
@ -14,6 +16,9 @@ namespace MWClass
/// can return an empty string.
static void registerSelf();
virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const;
///< Add reference into a cell for rendering
};
}

@ -2,6 +2,7 @@
#include "light.hpp"
#include <components/esm/loadligh.hpp>
#include <components/esm/lightstate.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -269,4 +270,24 @@ namespace MWClass
}
return std::make_pair(1,"");
}
void Light::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const
{
const ESM::LightState& state2 = dynamic_cast<const ESM::LightState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mTime = state2.mTime;
}
void Light::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const
{
ESM::LightState& state2 = dynamic_cast<ESM::LightState&> (state);
ensureCustomData (ptr);
state2.mTime = dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mTime;
}
}

@ -71,6 +71,14 @@ namespace MWClass
virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const;
std::pair<int, std::string> canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const;
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const;
///< Read additional state from \a state into \a ptr.
virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const;
///< Write additional state from \a ptr into \a state.
};
}

@ -155,11 +155,8 @@ namespace MWClass
int count = ptr.getRefData().getCount();
bool gold = isGold(ptr);
if (gold && ptr.getCellRef().mGoldValue != 1)
count = ptr.getCellRef().mGoldValue;
else if (gold)
count *= ref->mBase->mData.mValue;
if (gold)
count *= getValue(ptr);
std::string countString;
if (!gold)
@ -204,7 +201,7 @@ namespace MWClass
MWBase::Environment::get().getWorld()->getStore();
if (isGold(ptr)) {
int goldAmount = ptr.getRefData().getCount();
int goldAmount = getValue(ptr) * ptr.getRefData().getCount();
std::string base = "Gold_001";
if (goldAmount >= 100)
@ -223,6 +220,7 @@ namespace MWClass
newRef.getPtr().get<ESM::Miscellaneous>();
newPtr = MWWorld::Ptr(&cell.mMiscItems.insert(*ref), &cell);
newPtr.getCellRef().mGoldValue = goldAmount;
newPtr.getRefData().setCount(1);
} else {
MWWorld::LiveCellRef<ESM::Miscellaneous> *ref =
ptr.get<ESM::Miscellaneous>();

@ -7,6 +7,7 @@
#include <components/esm/loadmgef.hpp>
#include <components/esm/loadnpc.hpp>
#include <components/esm/npcstate.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -20,6 +21,7 @@
#include "../mwmechanics/movement.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "../mwmechanics/disease.hpp"
#include "../mwmechanics/combat.hpp"
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontalk.hpp"
@ -36,9 +38,6 @@
namespace
{
const Ogre::Radian kOgrePi (Ogre::Math::PI);
const Ogre::Radian kOgrePiOverTwo (Ogre::Math::PI / Ogre::Real(2.0));
struct CustomData : public MWWorld::CustomData
{
MWMechanics::NpcStats mNpcStats;
@ -144,7 +143,7 @@ namespace
*
* and by adding class, race, specialization bonus.
*/
void autoCalculateSkills(const ESM::NPC* npc, MWMechanics::NpcStats& npcStats)
void autoCalculateSkills(const ESM::NPC* npc, MWMechanics::NpcStats& npcStats, const MWWorld::Ptr& ptr)
{
const ESM::Class *class_ =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().find(npc->mClass);
@ -193,6 +192,18 @@ namespace
majorMultiplier = 1.0f;
break;
}
if (class_->mData.mSkills[k][1] == skillIndex)
{
// Major skill -> add starting spells for this skill if existing
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
MWWorld::Store<ESM::Spell>::iterator it = store.get<ESM::Spell>().begin();
for (; it != store.get<ESM::Spell>().end(); ++it)
{
if (it->mData.mFlags & ESM::Spell::F_Autocalc
&& MWMechanics::spellSchoolToSkill(MWMechanics::getSpellSchool(&*it, ptr)) == skillIndex)
npcStats.getSpells().add(it->mId);
}
}
}
// is this skill in the same Specialization as the class?
@ -243,6 +254,8 @@ namespace MWClass
fKnockDownMult = gmst.find("fKnockDownMult");
iKnockDownOddsMult = gmst.find("iKnockDownOddsMult");
iKnockDownOddsBase = gmst.find("iKnockDownOddsBase");
fDamageStrengthBase = gmst.find("fDamageStrengthBase");
fDamageStrengthMult = gmst.find("fDamageStrengthMult");
inited = true;
}
@ -305,7 +318,15 @@ namespace MWClass
data->mNpcStats.setReputation(ref->mBase->mNpdt12.mReputation);
autoCalculateAttributes(ref->mBase, data->mNpcStats);
autoCalculateSkills(ref->mBase, data->mNpcStats);
autoCalculateSkills(ref->mBase, data->mNpcStats, ptr);
}
// race powers
const ESM::Race *race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(ref->mBase->mRace);
for (std::vector<std::string>::const_iterator iter (race->mPowers.mList.begin());
iter!=race->mPowers.mList.end(); ++iter)
{
data->mNpcStats.getSpells().add (*iter);
}
if (data->mNpcStats.getFactionRanks().size())
@ -450,10 +471,11 @@ namespace MWClass
fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss);
getCreatureStats(ptr).setFatigue(fatigue);
float dist = 100.0f * (!weapon.isEmpty() ?
const float fCombatDistance = gmst.find("fCombatDistance")->getFloat();
float dist = fCombatDistance * (!weapon.isEmpty() ?
weapon.get<ESM::Weapon>()->mBase->mData.mReach :
gmst.find("fHandToHandReach")->getFloat());
// TODO: Use second to work out the hit angle
std::pair<MWWorld::Ptr, Ogre::Vector3> result = world->getHitContact(ptr, dist);
MWWorld::Ptr victim = result.first;
@ -497,16 +519,17 @@ namespace MWClass
{
const bool weaphashealth = get(weapon).hasItemHealth(weapon);
const unsigned char *attack = NULL;
if(type == MWMechanics::CreatureStats::AT_Chop)
if(type == ESM::Weapon::AT_Chop)
attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;
else if(type == MWMechanics::CreatureStats::AT_Slash)
else if(type == ESM::Weapon::AT_Slash)
attack = weapon.get<ESM::Weapon>()->mBase->mData.mSlash;
else if(type == MWMechanics::CreatureStats::AT_Thrust)
else if(type == ESM::Weapon::AT_Thrust)
attack = weapon.get<ESM::Weapon>()->mBase->mData.mThrust;
if(attack)
{
damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength());
damage *= 0.5f + (stats.getAttribute(ESM::Attribute::Luck).getModified() / 100.0f);
damage *= fDamageStrengthBase->getFloat() +
(stats.getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult->getFloat() * 0.1);
if(weaphashealth)
{
int weapmaxhealth = weapon.get<ESM::Weapon>()->mBase->mData.mHealth;
@ -514,7 +537,7 @@ namespace MWClass
weapon.getCellRef().mCharge = weapmaxhealth;
damage *= float(weapon.getCellRef().mCharge) / weapmaxhealth;
}
if (!MWBase::Environment::get().getWorld()->getGodModeState())
weapon.getCellRef().mCharge -= std::min(std::max(1,
(int)(damage * gmst.find("fWeaponDamageMult")->getFloat())), weapon.getCellRef().mCharge);
@ -579,33 +602,16 @@ namespace MWClass
enchantmentName);
if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
{
// Check if we have enough charges
const float enchantCost = enchantment->mData.mCost;
int eSkill = stats.getSkill(ESM::Skill::Enchant).getModified();
const int castCost = std::max(1.f, enchantCost - (enchantCost / 100) * (eSkill - 10));
if (weapon.getCellRef().mEnchantmentCharge == -1)
weapon.getCellRef().mEnchantmentCharge = enchantment->mData.mCharge;
if (weapon.getCellRef().mEnchantmentCharge < castCost)
{
if (ptr.getRefData().getHandle() == "player")
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}");
}
else
{
weapon.getCellRef().mEnchantmentCharge -= castCost;
MWMechanics::CastSpell cast(ptr, victim);
cast.cast(weapon);
if (ptr.getRefData().getHandle() == "player")
skillUsageSucceeded (ptr, ESM::Skill::Enchant, 3);
}
MWMechanics::CastSpell cast(ptr, victim);
cast.mHitPosition = hitPosition;
cast.cast(weapon);
}
}
// TODO: do not do this if the attack is blocked
if (healthdmg)
if (!weapon.isEmpty() && MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage))
damage = 0;
if (healthdmg && damage > 0)
MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition);
othercls.onHit(victim, damage, healthdmg, weapon, ptr, true);
@ -644,6 +650,9 @@ namespace MWClass
if (!attacker.isEmpty())
MWMechanics::diseaseContact(ptr, attacker);
if (damage > 0.0f && !object.isEmpty())
MWMechanics::resistNormalWeapon(ptr, attacker, object, damage);
if(damage > 0.0f)
{
// 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying
@ -715,6 +724,9 @@ namespace MWClass
if (armorref.mCharge == 0)
inv.unequipItem(armor, ptr);
if (ptr.getRefData().getHandle() == "player")
skillUsageSucceeded(ptr, get(armor).getEquipmentSkill(armor), 0);
switch(get(armor).getEquipmentSkill(armor))
{
case ESM::Skill::LightArmor:
@ -728,6 +740,8 @@ namespace MWClass
break;
}
}
else if(ptr.getRefData().getHandle() == "player")
skillUsageSucceeded(ptr, ESM::Skill::Unarmored, 0);
}
}
@ -746,6 +760,30 @@ namespace MWClass
}
}
void Npc::block(const MWWorld::Ptr &ptr) const
{
MWWorld::InventoryStore& inv = getInventoryStore(ptr);
MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
if (shield == inv.end())
return;
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
switch(shield->getClass().getEquipmentSkill(*shield))
{
case ESM::Skill::LightArmor:
sndMgr->playSound3D(ptr, "Light Armor Hit", 1.0f, 1.0f);
break;
case ESM::Skill::MediumArmor:
sndMgr->playSound3D(ptr, "Medium Armor Hit", 1.0f, 1.0f);
break;
case ESM::Skill::HeavyArmor:
sndMgr->playSound3D(ptr, "Heavy Armor Hit", 1.0f, 1.0f);
break;
default:
return;
}
}
void Npc::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const
{
MWMechanics::CreatureStats &crstats = getCreatureStats(ptr);
@ -964,7 +1002,7 @@ namespace MWClass
return ref->mBase->mFlags & ESM::NPC::Essential;
}
void Npc::registerSelf()
{
boost::shared_ptr<Class> instance (new Npc);
@ -1233,6 +1271,28 @@ namespace MWClass
return 0;
}
void Npc::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const
{
const ESM::NpcState& state2 = dynamic_cast<const ESM::NpcState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mInventoryStore.
readState (state2.mInventory);
}
void Npc::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const
{
ESM::NpcState& state2 = dynamic_cast<ESM::NpcState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mInventoryStore.
writeState (state2.mInventory);
}
const ESM::GameSetting *Npc::fMinWalkSpeed;
const ESM::GameSetting *Npc::fMaxWalkSpeed;
const ESM::GameSetting *Npc::fEncumberedMoveEffect;
@ -1252,4 +1312,7 @@ namespace MWClass
const ESM::GameSetting *Npc::fKnockDownMult;
const ESM::GameSetting *Npc::iKnockDownOddsMult;
const ESM::GameSetting *Npc::iKnockDownOddsBase;
const ESM::GameSetting *Npc::fDamageStrengthBase;
const ESM::GameSetting *Npc::fDamageStrengthMult;
}

@ -36,6 +36,8 @@ namespace MWClass
static const ESM::GameSetting *fKnockDownMult;
static const ESM::GameSetting *iKnockDownOddsMult;
static const ESM::GameSetting *iKnockDownOddsBase;
static const ESM::GameSetting *fDamageStrengthBase;
static const ESM::GameSetting *fDamageStrengthMult;
public:
@ -71,10 +73,14 @@ namespace MWClass
virtual MWWorld::InventoryStore& getInventoryStore (const MWWorld::Ptr& ptr) const;
///< Return inventory store
virtual bool hasInventoryStore(const MWWorld::Ptr &ptr) const { return true; }
virtual void hit(const MWWorld::Ptr& ptr, int type) const;
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const;
virtual void block(const MWWorld::Ptr &ptr) const;
virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const;
virtual boost::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
@ -131,7 +137,7 @@ namespace MWClass
///< Is \a ptr essential? (i.e. may losing \a ptr make the game unwinnable)
virtual int getServices (const MWWorld::Ptr& actor) const;
virtual bool isPersistent (const MWWorld::Ptr& ptr) const;
virtual std::string getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const;
@ -152,6 +158,14 @@ namespace MWClass
virtual bool isNpc() const {
return true;
}
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const;
///< Read additional state from \a state into \a ptr.
virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const;
///< Write additional state from \a ptr into \a state.
};
}

@ -429,12 +429,12 @@ namespace MWClass
return MWWorld::Ptr(&cell.mWeapons.insert(*ref), &cell);
}
float Weapon::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
int Weapon::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
{
MWWorld::LiveCellRef<ESM::Weapon> *ref =
ptr.get<ESM::Weapon>();
return ref->mBase->mData.mEnchant/10.f;
return ref->mBase->mData.mEnchant;
}
bool Weapon::canSell (const MWWorld::Ptr& item, int npcServices) const

@ -84,7 +84,7 @@ namespace MWClass
virtual float getWeight (const MWWorld::Ptr& ptr) const;
virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
virtual int getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
};
}

@ -286,7 +286,7 @@ namespace MWDialogue
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext), title);
MWBase::Environment::get().getJournal()->addTopic (topic, info->mId);
MWBase::Environment::get().getJournal()->addTopic (topic, info->mId, mActor.getClass().getName(mActor));
executeScript (info->mResultScript);
@ -420,7 +420,7 @@ namespace MWDialogue
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue);
// Apply disposition change to NPC's base disposition
if (mActor.getTypeName() == typeid(ESM::NPC).name())
if (mActor.getClass().isNpc())
{
MWMechanics::NpcStats& npcStats = MWWorld::Class::get(mActor).getNpcStats(mActor);
npcStats.setBaseDisposition(npcStats.getBaseDisposition() + mPermanentDispositionChange);
@ -451,7 +451,7 @@ namespace MWDialogue
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addResponse (Interpreter::fixDefinesDialog(text, interpreterContext));
MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId);
MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId, mActor.getClass().getName(mActor));
executeScript (info->mResultScript);
}
}
@ -586,7 +586,8 @@ namespace MWDialogue
MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager();
if(winMgr->getSubtitlesEnabled())
winMgr->messageBox(info->mResponse);
sndMgr->say(actor, info->mSound);
if (!info->mSound.empty())
sndMgr->say(actor, info->mSound);
}
}

@ -171,7 +171,7 @@ bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) c
// internally all globals are float :(
return select.selectCompare (
MWBase::Environment::get().getWorld()->getGlobalVariable (select.getName()).mFloat);
MWBase::Environment::get().getWorld()->getGlobalFloat (select.getName()));
case SelectWrapper::Function_Local:
{
@ -216,7 +216,7 @@ bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) c
float ratio = MWWorld::Class::get (player).getCreatureStats (player).getHealth().getCurrent() /
MWWorld::Class::get (player).getCreatureStats (player).getHealth().getModified();
return select.selectCompare (ratio);
return select.selectCompare (static_cast<int>(ratio*100));
}
case SelectWrapper::Function_PcDynamicStat:
@ -536,7 +536,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
case SelectWrapper::Function_CreatureTargetted:
return MWWorld::Class::get (mActor).getCreatureStats (mActor).getCreatureTargetted();
return mActor.getClass().getCreatureStats (mActor).getCreatureTargetted();
case SelectWrapper::Function_Werewolf:

@ -3,6 +3,8 @@
#include <stdexcept>
#include <components/esm/journalentry.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -10,23 +12,55 @@
namespace MWDialogue
{
JournalEntry::JournalEntry() {}
JournalEntry::JournalEntry (const std::string& topic, const std::string& infoId)
: mTopic (topic), mInfoId (infoId)
{}
Entry::Entry() {}
std::string JournalEntry::getText (const MWWorld::ESMStore& store) const
Entry::Entry (const std::string& topic, const std::string& infoId)
: mInfoId (infoId)
{
const ESM::Dialogue *dialogue =
store.get<ESM::Dialogue>().find (mTopic);
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find (topic);
for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
iter!=dialogue->mInfo.end(); ++iter)
if (iter->mId == mInfoId)
return iter->mResponse;
{
/// \todo text replacement
mText = iter->mResponse;
return;
}
throw std::runtime_error ("unknown info ID " + mInfoId + " for topic " + topic);
}
Entry::Entry (const ESM::JournalEntry& record) : mInfoId (record.mInfo), mText (record.mText), mActorName(record.mActorName) {}
std::string Entry::getText() const
{
return mText;
}
void Entry::write (ESM::JournalEntry& entry) const
{
entry.mInfo = mInfoId;
entry.mText = mText;
entry.mActorName = mActorName;
}
JournalEntry::JournalEntry() {}
JournalEntry::JournalEntry (const std::string& topic, const std::string& infoId)
: Entry (topic, infoId), mTopic (topic)
{}
JournalEntry::JournalEntry (const ESM::JournalEntry& record)
: Entry (record), mTopic (record.mTopic)
{}
throw std::runtime_error ("unknown info ID " + mInfoId + " for topic " + mTopic);
void JournalEntry::write (ESM::JournalEntry& entry) const
{
Entry::write (entry);
entry.mTopic = mTopic;
}
JournalEntry JournalEntry::makeFromQuest (const std::string& topic, int index)
@ -49,6 +83,7 @@ namespace MWDialogue
throw std::runtime_error ("unknown journal index for topic " + topic);
}
StampedJournalEntry::StampedJournalEntry()
: mDay (0), mMonth (0), mDayOfMonth (0)
{}
@ -58,11 +93,24 @@ namespace MWDialogue
: JournalEntry (topic, infoId), mDay (day), mMonth (month), mDayOfMonth (dayOfMonth)
{}
StampedJournalEntry::StampedJournalEntry (const ESM::JournalEntry& record)
: JournalEntry (record), mDay (record.mDay), mMonth (record.mMonth),
mDayOfMonth (record.mDayOfMonth)
{}
void StampedJournalEntry::write (ESM::JournalEntry& entry) const
{
JournalEntry::write (entry);
entry.mDay = mDay;
entry.mMonth = mMonth;
entry.mDayOfMonth = mDayOfMonth;
}
StampedJournalEntry StampedJournalEntry::makeFromQuest (const std::string& topic, int index)
{
int day = MWBase::Environment::get().getWorld()->getGlobalVariable ("dayspassed").mLong;
int month = MWBase::Environment::get().getWorld()->getGlobalVariable ("month").mLong;
int dayOfMonth = MWBase::Environment::get().getWorld()->getGlobalVariable ("day").mLong;
int day = MWBase::Environment::get().getWorld()->getGlobalInt ("dayspassed");
int month = MWBase::Environment::get().getWorld()->getGlobalInt ("month");
int dayOfMonth = MWBase::Environment::get().getWorld()->getGlobalInt ("day");
return StampedJournalEntry (topic, idFromIndex (topic, index), day, month, dayOfMonth);
}

@ -3,24 +3,45 @@
#include <string>
namespace MWWorld
namespace ESM
{
struct ESMStore;
struct JournalEntry;
}
namespace MWDialogue
{
/// \brief A quest or dialogue entry
struct JournalEntry
/// \brief Basic quest/dialogue/topic entry
struct Entry
{
std::string mTopic;
std::string mInfoId;
std::string mText;
std::string mActorName; // optional
Entry();
Entry (const std::string& topic, const std::string& infoId);
Entry (const ESM::JournalEntry& record);
std::string getText() const;
void write (ESM::JournalEntry& entry) const;
};
/// \brief A dialogue entry
///
/// Same as entry, but store TopicID
struct JournalEntry : public Entry
{
std::string mTopic;
JournalEntry();
JournalEntry (const std::string& topic, const std::string& infoId);
std::string getText (const MWWorld::ESMStore& store) const;
JournalEntry (const ESM::JournalEntry& record);
void write (ESM::JournalEntry& entry) const;
static JournalEntry makeFromQuest (const std::string& topic, int index);
@ -39,6 +60,10 @@ namespace MWDialogue
StampedJournalEntry (const std::string& topic, const std::string& infoId,
int day, int month, int dayOfMonth);
StampedJournalEntry (const ESM::JournalEntry& record);
void write (ESM::JournalEntry& entry) const;
static StampedJournalEntry makeFromQuest (const std::string& topic, int index);
};
}

@ -1,6 +1,13 @@
#include "journalimp.hpp"
#include <iterator>
#include <components/esm/esmwriter.hpp>
#include <components/esm/esmreader.hpp>
#include <components/esm/queststate.hpp>
#include <components/esm/journalentry.hpp>
#include "../mwworld/esmstore.hpp"
#include "../mwbase/environment.hpp"
@ -26,6 +33,38 @@ namespace MWDialogue
return iter->second;
}
Topic& Journal::getTopic (const std::string& id)
{
TTopicContainer::iterator iter = mTopics.find (id);
if (iter==mTopics.end())
{
std::pair<TTopicContainer::iterator, bool> result
= mTopics.insert (std::make_pair (id, Topic (id)));
iter = result.first;
}
return iter->second;
}
bool Journal::isThere (const std::string& topicId, const std::string& infoId) const
{
if (const ESM::Dialogue *dialogue =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().search (topicId))
{
if (infoId.empty())
return true;
for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
iter!=dialogue->mInfo.end(); ++iter)
if (iter->mId == infoId)
return true;
}
return false;
}
Journal::Journal()
{}
@ -64,19 +103,13 @@ namespace MWDialogue
quest.setIndex (index);
}
void Journal::addTopic (const std::string& topicId, const std::string& infoId)
void Journal::addTopic (const std::string& topicId, const std::string& infoId, const std::string& actorName)
{
TTopicContainer::iterator iter = mTopics.find (topicId);
if (iter==mTopics.end())
{
std::pair<TTopicContainer::iterator, bool> result
= mTopics.insert (std::make_pair (topicId, Topic (topicId)));
iter = result.first;
}
Topic& topic = getTopic (topicId);
iter->second.addEntry (JournalEntry (topicId, infoId));
JournalEntry entry(topicId, infoId);
entry.mActorName = actorName;
topic.addEntry (entry);
}
int Journal::getJournalIndex (const std::string& id) const
@ -118,4 +151,106 @@ namespace MWDialogue
{
return mTopics.end();
}
int Journal::countSavedGameRecords() const
{
int count = static_cast<int> (mQuests.size());
for (TQuestIter iter (mQuests.begin()); iter!=mQuests.end(); ++iter)
count += std::distance (iter->second.begin(), iter->second.end());
count += std::distance (mJournal.begin(), mJournal.end());
for (TTopicIter iter (mTopics.begin()); iter!=mTopics.end(); ++iter)
count += std::distance (iter->second.begin(), iter->second.end());
return count;
}
void Journal::write (ESM::ESMWriter& writer) const
{
for (TQuestIter iter (mQuests.begin()); iter!=mQuests.end(); ++iter)
{
const Quest& quest = iter->second;
ESM::QuestState state;
quest.write (state);
writer.startRecord (ESM::REC_QUES);
state.save (writer);
writer.endRecord (ESM::REC_QUES);
for (Topic::TEntryIter iter (quest.begin()); iter!=quest.end(); ++iter)
{
ESM::JournalEntry entry;
entry.mType = ESM::JournalEntry::Type_Quest;
entry.mTopic = quest.getTopic();
iter->write (entry);
writer.startRecord (ESM::REC_JOUR);
entry.save (writer);
writer.endRecord (ESM::REC_JOUR);
}
}
for (TEntryIter iter (mJournal.begin()); iter!=mJournal.end(); ++iter)
{
ESM::JournalEntry entry;
entry.mType = ESM::JournalEntry::Type_Journal;
iter->write (entry);
writer.startRecord (ESM::REC_JOUR);
entry.save (writer);
writer.endRecord (ESM::REC_JOUR);
}
for (TTopicIter iter (mTopics.begin()); iter!=mTopics.end(); ++iter)
{
const Topic& topic = iter->second;
for (Topic::TEntryIter iter (topic.begin()); iter!=topic.end(); ++iter)
{
ESM::JournalEntry entry;
entry.mType = ESM::JournalEntry::Type_Topic;
entry.mTopic = topic.getTopic();
iter->write (entry);
writer.startRecord (ESM::REC_JOUR);
entry.save (writer);
writer.endRecord (ESM::REC_JOUR);
}
}
}
void Journal::readRecord (ESM::ESMReader& reader, int32_t type)
{
if (type==ESM::REC_JOUR)
{
ESM::JournalEntry record;
record.load (reader);
if (isThere (record.mTopic, record.mInfo))
switch (record.mType)
{
case ESM::JournalEntry::Type_Quest:
getQuest (record.mTopic).insertEntry (record);
break;
case ESM::JournalEntry::Type_Journal:
mJournal.push_back (record);
break;
case ESM::JournalEntry::Type_Topic:
getTopic (record.mTopic).insertEntry (record);
break;
}
}
else if (type==ESM::REC_QUES)
{
ESM::QuestState record;
record.load (reader);
if (isThere (record.mTopic))
mQuests.insert (std::make_pair (record.mTopic, record));
}
}
}

@ -15,8 +15,14 @@ namespace MWDialogue
TQuestContainer mQuests;
TTopicContainer mTopics;
private:
Quest& getQuest (const std::string& id);
Topic& getTopic (const std::string& id);
bool isThere (const std::string& topicId, const std::string& infoId = "") const;
public:
Journal();
@ -32,7 +38,7 @@ namespace MWDialogue
virtual int getJournalIndex (const std::string& id) const;
///< Get the journal index.
virtual void addTopic (const std::string& topicId, const std::string& infoId);
virtual void addTopic (const std::string& topicId, const std::string& infoId, const std::string& actorName);
virtual TEntryIter begin() const;
///< Iterator pointing to the begin of the main journal.
@ -55,6 +61,12 @@ namespace MWDialogue
virtual TTopicIter topicEnd() const;
///< Iterator pointing past the last topic.
virtual int countSavedGameRecords() const;
virtual void write (ESM::ESMWriter& writer) const;
virtual void readRecord (ESM::ESMReader& reader, int32_t type);
};
}

@ -1,6 +1,8 @@
#include "quest.hpp"
#include <components/esm/queststate.hpp>
#include "../mwworld/esmstore.hpp"
#include "../mwbase/environment.hpp"
@ -16,7 +18,11 @@ namespace MWDialogue
: Topic (topic), mIndex (0), mFinished (false)
{}
const std::string Quest::getName() const
Quest::Quest (const ESM::QuestState& state)
: Topic (state.mTopic), mIndex (state.mState), mFinished (state.mFinished!=0)
{}
std::string Quest::getName() const
{
const ESM::Dialogue *dialogue =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find (mTopic);
@ -39,21 +45,24 @@ namespace MWDialogue
const ESM::Dialogue *dialogue =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find (mTopic);
bool found=false;
for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
iter!=dialogue->mInfo.end(); ++iter)
if (iter->mData.mDisposition==index && iter->mQuestStatus!=ESM::DialInfo::QS_Name)
{
mIndex = index;
if (iter->mQuestStatus==ESM::DialInfo::QS_Finished)
mFinished = true;
else if (iter->mQuestStatus==ESM::DialInfo::QS_Restart)
mFinished = false;
return;
found = true;
// Don't return here. Quest status may actually be in a different info record, since we don't merge these (yet?)
}
throw std::runtime_error ("unknown journal index for topic " + mTopic);
if (found)
mIndex = index;
else
throw std::runtime_error ("unknown journal index for topic " + mTopic);
}
bool Quest::isFinished() const
@ -79,12 +88,20 @@ namespace MWDialogue
if (index==-1)
throw std::runtime_error ("unknown journal entry for topic " + mTopic);
setIndex (index);
if (index > mIndex)
setIndex (index);
for (TEntryIter iter (mEntries.begin()); iter!=mEntries.end(); ++iter)
if (*iter==entry.mInfoId)
if (iter->mInfoId==entry.mInfoId)
return;
mEntries.push_back (entry.mInfoId);
mEntries.push_back (entry); // we want slicing here
}
void Quest::write (ESM::QuestState& state) const
{
state.mTopic = getTopic();
state.mState = mIndex;
state.mFinished = mFinished;
}
}

@ -3,9 +3,14 @@
#include "topic.hpp"
namespace ESM
{
struct QuestState;
}
namespace MWDialogue
{
/// \brief A quest in progress or a compelted quest
/// \brief A quest in progress or a completed quest
class Quest : public Topic
{
int mIndex;
@ -17,13 +22,15 @@ namespace MWDialogue
Quest (const std::string& topic);
const std::string getName() const;
Quest (const ESM::QuestState& state);
virtual std::string getName() const;
///< May be an empty string
int getIndex() const;
void setIndex (int index);
///< Calling this function with a non-existant index while throw an exception.
///< Calling this function with a non-existent index will throw an exception.
bool isFinished() const;
@ -31,6 +38,8 @@ namespace MWDialogue
///< Add entry and adjust index accordingly.
///
/// \note Redundant entries are ignored, but the index is still adjusted.
void write (ESM::QuestState& state) const;
};
}

@ -1,6 +1,9 @@
#include "topic.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/esmstore.hpp"
namespace MWDialogue
@ -9,7 +12,8 @@ namespace MWDialogue
{}
Topic::Topic (const std::string& topic)
: mTopic (topic)
: mTopic (topic), mName (
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find (topic)->mId)
{}
Topic::~Topic()
@ -20,11 +24,29 @@ namespace MWDialogue
if (entry.mTopic!=mTopic)
throw std::runtime_error ("topic does not match: " + mTopic);
for (TEntryIter iter = begin(); iter!=end(); ++iter)
if (*iter==entry.mInfoId)
// bail out if we already have heard this
for (Topic::TEntryIter it = mEntries.begin(); it != mEntries.end(); ++it)
{
if (it->mInfoId == entry.mInfoId)
return;
}
mEntries.push_back (entry); // we want slicing here
}
void Topic::insertEntry (const ESM::JournalEntry& entry)
{
mEntries.push_back (entry);
}
mEntries.push_back (entry.mInfoId);
std::string Topic::getTopic() const
{
return mTopic;
}
std::string Topic::getName() const
{
return mName;
}
Topic::TEntryIter Topic::begin() const

@ -6,6 +6,11 @@
#include "journalentry.hpp"
namespace ESM
{
struct JournalEntry;
}
namespace MWDialogue
{
/// \brief Collection of seen responses for a topic
@ -13,13 +18,14 @@ namespace MWDialogue
{
public:
typedef std::vector<std::string> TEntryContainer;
typedef std::vector<Entry> TEntryContainer;
typedef TEntryContainer::const_iterator TEntryIter;
protected:
std::string mTopic;
TEntryContainer mEntries; // info-IDs
std::string mName;
TEntryContainer mEntries;
public:
@ -34,7 +40,13 @@ namespace MWDialogue
///
/// \note Redundant entries are ignored.
std::string const & getName () const { return mTopic; }
void insertEntry (const ESM::JournalEntry& entry);
///< Add entry without checking for redundant entries or modifying the state of the
/// topic otherwise
std::string getTopic() const;
virtual std::string getName() const;
TEntryIter begin() const;
///< Iterator pointing to the begin of the journal for this topic.

@ -33,8 +33,7 @@ namespace MWGui
getWidget(mBirthList, "BirthsignList");
mBirthList->setScrollVisible(true);
mBirthList->eventListSelectAccept += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth);
mBirthList->eventListMouseItemActivate += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth);
mBirthList->eventListSelectAccept += MyGUI::newDelegate(this, &BirthDialog::onAccept);
mBirthList->eventListChangePosition += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth);
MyGUI::Button* backButton;
@ -97,6 +96,14 @@ namespace MWGui
eventDone(this);
}
void BirthDialog::onAccept(MyGUI::ListBox *_sender, size_t _index)
{
onSelectBirth(_sender, _index);
if(mBirthList->getIndexSelected() == MyGUI::ITEM_NONE)
return;
eventDone(this);
}
void BirthDialog::onBackClicked(MyGUI::Widget* _sender)
{
eventBack();

@ -38,6 +38,7 @@ namespace MWGui
protected:
void onSelectBirth(MyGUI::ListBox* _sender, size_t _index);
void onAccept(MyGUI::ListBox* _sender, size_t index);
void onOkClicked(MyGUI::Widget* _sender);
void onBackClicked(MyGUI::Widget* _sender);

@ -10,7 +10,7 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/npcstats.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/fallback.hpp"
@ -47,9 +47,8 @@ namespace
void updatePlayerHealth()
{
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats(player);
creatureStats.updateHealth();
MWMechanics::NpcStats& npcStats = MWWorld::Class::get(player).getNpcStats(player);
npcStats.updateHealth();
}
}

@ -82,8 +82,7 @@ namespace MWGui
getWidget(mClassList, "ClassList");
mClassList->setScrollVisible(true);
mClassList->eventListSelectAccept += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass);
mClassList->eventListMouseItemActivate += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass);
mClassList->eventListSelectAccept += MyGUI::newDelegate(this, &PickClassDialog::onAccept);
mClassList->eventListChangePosition += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass);
getWidget(mClassImage, "ClassImage");
@ -152,6 +151,14 @@ namespace MWGui
eventBack();
}
void PickClassDialog::onAccept(MyGUI::ListBox* _sender, size_t _index)
{
onSelectClass(_sender, _index);
if(mClassList->getIndexSelected() == MyGUI::ITEM_NONE)
return;
eventDone(this);
}
void PickClassDialog::onSelectClass(MyGUI::ListBox* _sender, size_t _index)
{
if (_index == MyGUI::ITEM_NONE)

@ -111,6 +111,7 @@ namespace MWGui
protected:
void onSelectClass(MyGUI::ListBox* _sender, size_t _index);
void onAccept(MyGUI::ListBox* _sender, size_t _index);
void onOkClicked(MyGUI::Widget* _sender);
void onBackClicked(MyGUI::Widget* _sender);

@ -12,7 +12,7 @@ namespace MWGui
void CompanionItemModel::copyItem (const ItemStack& item, size_t count)
{
if (mActor.getTypeName() == typeid(ESM::NPC).name())
if (mActor.getClass().isNpc())
{
MWMechanics::NpcStats& stats = MWWorld::Class::get(mActor).getNpcStats(mActor);
stats.modifyProfit(MWWorld::Class::get(item.mBase).getValue(item.mBase) * count);
@ -23,7 +23,7 @@ namespace MWGui
void CompanionItemModel::removeItem (const ItemStack& item, size_t count)
{
if (mActor.getTypeName() == typeid(ESM::NPC).name())
if (mActor.getClass().isNpc())
{
MWMechanics::NpcStats& stats = MWWorld::Class::get(mActor).getNpcStats(mActor);
stats.modifyProfit(-MWWorld::Class::get(item.mBase).getValue(item.mBase) * count);

@ -235,7 +235,7 @@ namespace MWGui
mItemView->setModel (mSortModel);
MyGUI::InputManager::getInstance().setKeyFocusWidget(mCloseButton);
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton);
// Careful here. setTitle may cause size updates, causing itemview redraw, so make sure to do it last
// or we end up using a possibly invalid model.

@ -5,6 +5,8 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/dialoguemanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/containerstore.hpp"
@ -296,6 +298,28 @@ namespace MWGui
return;
}
// check if the player is attempting to use a soulstone or item that was stolen from this actor
if (mPtr != player)
{
for (int i=0; i<2; ++i)
{
MWWorld::Ptr item = (i == 0) ? mEnchanting.getOldItem() : mEnchanting.getGem();
if (Misc::StringUtils::ciEqual(item.getCellRef().mOwner, mPtr.getCellRef().mRefID))
{
std::string msg = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sNotifyMessage49")->getString();
if (msg.find("%s") != std::string::npos)
msg.replace(msg.find("%s"), 2, item.getClass().getName(item));
MWBase::Environment::get().getWindowManager()->messageBox(msg);
MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief");
MWBase::Environment::get().getMechanicsManager()->reportCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft,
item.getClass().getValue(item));
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting);
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Dialogue);
return;
}
}
}
int result = mEnchanting.create();
if(result==1)

@ -12,6 +12,8 @@
#include <boost/range/algorithm/copy.hpp>
#include <OgreUTFString.h>
#include <OgreResourceGroupManager.h>
namespace
{
int convertFromHex(std::string hex)
@ -288,6 +290,16 @@ namespace MWGui
MyGUI::ImageBox* box = mParent->createWidget<MyGUI::ImageBox> ("ImageBox",
MyGUI::IntCoord(0, mHeight, width, height), MyGUI::Align::Left | MyGUI::Align::Top,
mParent->getName() + boost::lexical_cast<std::string>(mParent->getChildCount()));
// Apparently a bug with some morrowind versions, they reference the image without the size suffix.
// So if the image isn't found, try appending the size.
if (!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("bookart\\"+image))
{
std::stringstream str;
str << image.substr(0, image.rfind(".")) << "_" << width << "_" << height << image.substr(image.rfind("."));
image = str.str();
}
box->setImageTexture("bookart\\" + image);
box->setProperty("NeedMouse", "false");
}

@ -74,9 +74,9 @@ void InventoryItemModel::update()
ItemStack newItem (item, this, item.getRefData().getCount());
if (mActor.getTypeName() == typeid(ESM::NPC).name())
if (mActor.getClass().hasInventoryStore(mActor))
{
MWWorld::InventoryStore& store = MWWorld::Class::get(mActor).getInventoryStore(mActor);
MWWorld::InventoryStore& store = mActor.getClass().getInventoryStore(mActor);
for (int slot=0; slot<MWWorld::InventoryStore::Slots; ++slot)
{
MWWorld::ContainerStoreIterator equipped = store.getSlot(slot);

@ -196,34 +196,6 @@ book JournalBooks::createEmptyJournalBook ()
typesetter->lineBreak ();
typesetter->write (body, to_utf8_span ("You should have gone though the starting quest and got an initial quest."));
BookTypesetter::Style* big = typesetter->createStyle ("", MyGUI::Colour::Black);
BookTypesetter::Style* test = typesetter->createStyle ("MonoFont", MyGUI::Colour::Blue);
typesetter->sectionBreak (20);
typesetter->write (body, to_utf8_span (
"The layout engine doesn't currently support aligning fonts to "
"their baseline within a single line so the following text looks "
"funny. In order to properly implement it, a stupidly simple "
"change is needed in MyGUI to report the where the baseline is for "
"a particular font"
));
typesetter->sectionBreak (20);
typesetter->write (big, to_utf8_span ("big text g"));
typesetter->write (body, to_utf8_span (" проверяем только в дебаге"));
typesetter->write (body, to_utf8_span (" normal g"));
typesetter->write (big, to_utf8_span (" done g"));
typesetter->sectionBreak (20);
typesetter->write (test, to_utf8_span (
"int main (int argc,\n"
" char ** argv)\n"
"{\n"
" print (\"hello world!\\n\");\n"
" return 0;\n"
"}\n"
));
return typesetter->complete ();
}

@ -20,8 +20,6 @@ namespace MWGui {
struct JournalViewModelImpl;
static void injectMonthName (std::ostream & os, int month);
struct JournalViewModelImpl : JournalViewModel
{
typedef KeywordSearch <std::string, intptr_t> KeywordSearchT;
@ -206,10 +204,12 @@ struct JournalViewModelImpl : JournalViewModel
if (active_only && i->second.isFinished ())
continue;
/// \todo quest.getName() is broken? returns empty string
//const MWDialogue::Quest& quest = i->second;
visitor (reinterpret_cast <QuestId> (&i->second), toUtf8Span (i->first));
const MWDialogue::Quest& quest = i->second;
// Unfortunately Morrowind.esm has no quest names, since the quest book was added with tribunal.
if (quest.getName().empty())
visitor (reinterpret_cast <QuestId> (&i->second), toUtf8Span (i->first));
else
visitor (reinterpret_cast <QuestId> (&i->second), toUtf8Span (quest.getName()));
}
}
@ -235,21 +235,21 @@ struct JournalViewModelImpl : JournalViewModel
std::string getText () const
{
return itr->getText(MWBase::Environment::get().getWorld()->getStore());
return itr->getText();
}
Utf8Span timestamp () const
{
if (timestamp_buffer.empty ())
{
std::ostringstream os;
std::string dayStr = MyGUI::LanguageManager::getInstance().replaceTags("#{sDay}");
os << itr->mDayOfMonth << ' ';
injectMonthName (os, itr->mMonth);
std::ostringstream os;
const std::string& dayStr = MyGUI::LanguageManager::getInstance().replaceTags("#{sDay}");
os << " (" << dayStr << " " << (itr->mDay + 1) << ')';
os
<< itr->mDayOfMonth << ' '
<< MWBase::Environment::get().getWorld()->getMonthName (itr->mMonth)
<< " (" << dayStr << " " << (itr->mDay + 1) << ')';
timestamp_buffer = os.str ();
}
@ -270,7 +270,7 @@ struct JournalViewModelImpl : JournalViewModel
{
for (MWDialogue::Topic::TEntryIter j = quest->begin (); j != quest->end (); ++j)
{
if (i->mInfoId == *j)
if (i->mInfoId == j->mInfoId)
visitor (JournalEntryImpl <MWBase::Journal::TEntryIter> (this, i));
}
}
@ -290,9 +290,7 @@ struct JournalViewModelImpl : JournalViewModel
void visitTopicName (TopicId topicId, boost::function <void (Utf8Span)> visitor) const
{
MWDialogue::Topic const & topic = * reinterpret_cast <MWDialogue::Topic const *> (topicId);
// This is to get the correct case for the topic
const std::string& name = MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find(topic.getName())->mId;
visitor (toUtf8Span (name));
visitor (toUtf8Span (topic.getName()));
}
void visitTopicNamesStartingWith (char character, boost::function < void (TopicId , Utf8Span) > visitor) const
@ -304,10 +302,7 @@ struct JournalViewModelImpl : JournalViewModel
if (i->first [0] != std::tolower (character, mLocale))
continue;
// This is to get the correct case for the topic
const std::string& name = MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find(i->first)->mId;
visitor (TopicId (&i->second), toUtf8Span (name));
visitor (TopicId (&i->second), toUtf8Span (i->second.getName()));
}
}
@ -316,24 +311,18 @@ struct JournalViewModelImpl : JournalViewModel
{
MWDialogue::Topic const & mTopic;
mutable std::string source_buffer;
TopicEntryImpl (JournalViewModelImpl const * model, MWDialogue::Topic const & topic, iterator_t itr) :
BaseEntry (model, itr), mTopic (topic)
{}
std::string getText () const
{
/// \todo defines are not replaced (%PCName etc). should probably be done elsewhere though since we need the actor
return mTopic.getEntry (*itr).getText(MWBase::Environment::get().getWorld()->getStore());
return itr->getText();
}
Utf8Span source () const
{
if (source_buffer.empty ())
source_buffer = "someone";
return toUtf8Span (source_buffer);
return toUtf8Span (itr->mActorName);
}
};
@ -349,38 +338,6 @@ struct JournalViewModelImpl : JournalViewModel
}
};
static void injectMonthName (std::ostream & os, int month)
{
MyGUI::LanguageManager& lm = MyGUI::LanguageManager::getInstance();
if (month == 0)
os << lm.replaceTags ("#{sMonthMorningstar}");
else if (month == 1)
os << lm.replaceTags ("#{sMonthSunsdawn}");
else if (month == 2)
os << lm.replaceTags ("#{sMonthFirstseed}");
else if (month == 3)
os << lm.replaceTags ("#{sMonthRainshand}");
else if (month == 4)
os << lm.replaceTags ("#{sMonthSecondseed}");
else if (month == 5)
os << lm.replaceTags ("#{sMonthMidyear}");
else if (month == 6)
os << lm.replaceTags ("#{sMonthSunsheight}");
else if (month == 7)
os << lm.replaceTags ("#{sMonthLastseed}");
else if (month == 8)
os << lm.replaceTags ("#{sMonthHeartfire}");
else if (month == 9)
os << lm.replaceTags ("#{sMonthFrostfall}");
else if (month == 10)
os << lm.replaceTags ("#{sMonthSunsdusk}");
else if (month == 11)
os << lm.replaceTags ("#{sMonthEveningstar}");
else
os << month;
}
JournalViewModel::Ptr JournalViewModel::create ()
{
return boost::make_shared <JournalViewModelImpl> ();

@ -155,7 +155,6 @@ namespace MWGui
void LevelupDialog::onOkButtonClicked (MyGUI::Widget* sender)
{
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats (player);
MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player);
if (mSpentAttributes.size() < 3)
@ -165,15 +164,14 @@ namespace MWGui
// increase attributes
for (int i=0; i<3; ++i)
{
MWMechanics::AttributeValue attribute = creatureStats.getAttribute(mSpentAttributes[i]);
MWMechanics::AttributeValue attribute = pcStats.getAttribute(mSpentAttributes[i]);
attribute.setBase (attribute.getBase () + pcStats.getLevelupAttributeMultiplier (mSpentAttributes[i]));
if (attribute.getBase() >= 100)
attribute.setBase(100);
creatureStats.setAttribute(mSpentAttributes[i], attribute);
pcStats.setAttribute(mSpentAttributes[i], attribute);
}
creatureStats.levelUp();
pcStats.levelUp ();
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Levelup);

@ -1,13 +1,14 @@
#include "mainmenu.hpp"
#include <OgreRoot.h>
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/journal.hpp"
#include "../mwbase/dialoguemanager.hpp"
#include "../mwbase/statemanager.hpp"
#include "../mwstate/character.hpp"
#include "savegamedialog.hpp"
@ -16,90 +17,132 @@ namespace MWGui
MainMenu::MainMenu(int w, int h)
: OEngine::GUI::Layout("openmw_mainmenu.layout")
, mButtonBox(0)
, mButtonBox(0), mWidth (w), mHeight (h)
, mSaveGameDialog(NULL)
{
updateMenu();
}
MainMenu::~MainMenu()
{
onResChange(w,h);
delete mSaveGameDialog;
}
void MainMenu::onResChange(int w, int h)
{
setCoord(0,0,w,h);
mWidth = w;
mHeight = h;
updateMenu();
}
void MainMenu::setVisible (bool visible)
{
if (visible)
updateMenu();
OEngine::GUI::Layout::setVisible (visible);
}
void MainMenu::onButtonClicked(MyGUI::Widget *sender)
{
std::string name = *sender->getUserData<std::string>();
MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f);
if (name == "return")
{
MWBase::Environment::get().getSoundManager ()->resumeSounds (MWBase::SoundManager::Play_TypeSfx);
MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu);
}
else if (name == "options")
MWBase::Environment::get().getWindowManager ()->pushGuiMode (GM_Settings);
else if (name == "exitgame")
MWBase::Environment::get().getStateManager()->requestQuit();
else if (name == "newgame")
{
MWBase::Environment::get().getStateManager()->newGame();
}
else
{
if (!mSaveGameDialog)
mSaveGameDialog = new SaveGameDialog();
if (name == "loadgame")
mSaveGameDialog->setLoadOrSave(true);
else if (name == "savegame")
mSaveGameDialog->setLoadOrSave(false);
mSaveGameDialog->setVisible(true);
}
}
void MainMenu::updateMenu()
{
setCoord(0,0, mWidth, mHeight);
if (mButtonBox)
MyGUI::Gui::getInstance ().destroyWidget(mButtonBox);
if (!mButtonBox)
mButtonBox = mMainWidget->createWidget<MyGUI::Widget>("", MyGUI::IntCoord(0, 0, 0, 0), MyGUI::Align::Default);
mButtonBox = mMainWidget->createWidget<MyGUI::Widget>("", MyGUI::IntCoord(0, 0, 0, 0), MyGUI::Align::Default);
int curH = 0;
MWBase::StateManager::State state = MWBase::Environment::get().getStateManager()->getState();
std::vector<std::string> buttons;
buttons.push_back("return");
if (state==MWBase::StateManager::State_Running)
buttons.push_back("return");
buttons.push_back("newgame");
//buttons.push_back("loadgame");
//buttons.push_back("savegame");
if (MWBase::Environment::get().getStateManager()->characterBegin()!=
MWBase::Environment::get().getStateManager()->characterEnd())
buttons.push_back("loadgame");
if (state==MWBase::StateManager::State_Running &&
MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1)
buttons.push_back("savegame");
buttons.push_back("options");
//buttons.push_back("credits");
buttons.push_back("exitgame");
int maxwidth = 0;
mButtons.clear();
// Create new buttons if needed
for (std::vector<std::string>::iterator it = buttons.begin(); it != buttons.end(); ++it)
{
MWGui::ImageButton* button = mButtonBox->createWidget<MWGui::ImageButton>
("ImageBox", MyGUI::IntCoord(0, curH, 0, 0), MyGUI::Align::Default);
button->setProperty("ImageHighlighted", "textures\\menu_" + *it + "_over.dds");
button->setProperty("ImageNormal", "textures\\menu_" + *it + ".dds");
button->setProperty("ImagePushed", "textures\\menu_" + *it + "_pressed.dds");
MyGUI::IntSize requested = button->getRequestedSize();
button->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::onButtonClicked);
mButtons[*it] = button;
curH += requested.height;
if (requested.width > maxwidth)
maxwidth = requested.width;
if (mButtons.find(*it) == mButtons.end())
{
MWGui::ImageButton* button = mButtonBox->createWidget<MWGui::ImageButton>
("ImageBox", MyGUI::IntCoord(0, curH, 0, 0), MyGUI::Align::Default);
button->setProperty("ImageHighlighted", "textures\\menu_" + *it + "_over.dds");
button->setProperty("ImageNormal", "textures\\menu_" + *it + ".dds");
button->setProperty("ImagePushed", "textures\\menu_" + *it + "_pressed.dds");
button->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::onButtonClicked);
button->setUserData(std::string(*it));
mButtons[*it] = button;
}
}
// Start by hiding all buttons
int maxwidth = 0;
for (std::map<std::string, MWGui::ImageButton*>::iterator it = mButtons.begin(); it != mButtons.end(); ++it)
{
it->second->setVisible(false);
MyGUI::IntSize requested = it->second->getRequestedSize();
it->second->setCoord((maxwidth-requested.width) / 2, it->second->getTop(), requested.width, requested.height);
if (requested.width > maxwidth)
maxwidth = requested.width;
}
mButtonBox->setCoord (w/2 - maxwidth/2, h/2 - curH/2, maxwidth, curH);
}
void MainMenu::onButtonClicked(MyGUI::Widget *sender)
{
MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f);
if (sender == mButtons["return"])
{
MWBase::Environment::get().getSoundManager ()->resumeSounds (MWBase::SoundManager::Play_TypeSfx);
MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu);
}
else if (sender == mButtons["options"])
MWBase::Environment::get().getWindowManager ()->pushGuiMode (GM_Settings);
else if (sender == mButtons["exitgame"])
MWBase::Environment::get().setRequestExit();
else if (sender == mButtons["newgame"])
// Now show and position the ones we want
for (std::vector<std::string>::iterator it = buttons.begin(); it != buttons.end(); ++it)
{
MWBase::Environment::get().getWorld()->startNewGame();
MWBase::Environment::get().getWindowManager()->setNewGame(true);
MWBase::Environment::get().getDialogueManager()->clear();
MWBase::Environment::get().getJournal()->clear();
assert(mButtons.find(*it) != mButtons.end());
MWGui::ImageButton* button = mButtons[*it];
button->setVisible(true);
MyGUI::IntSize requested = button->getRequestedSize();
button->setCoord((maxwidth-requested.width) / 2, curH, requested.width, requested.height);
curH += requested.height;
}
else if (sender == mButtons["loadgame"])
{
MWGui::SaveGameDialog* dialog = new MWGui::SaveGameDialog();
dialog->setLoadOrSave(true);
dialog->setVisible(true);
}
else if (sender == mButtons["savegame"])
{
MWGui::SaveGameDialog* dialog = new MWGui::SaveGameDialog();
dialog->setLoadOrSave(false);
dialog->setVisible(true);
}
}
mButtonBox->setCoord (mWidth/2 - maxwidth/2, mHeight/2 - curH/2, maxwidth, curH);
}
}

@ -5,19 +5,33 @@
namespace MWGui
{
class SaveGameDialog;
class MainMenu : public OEngine::GUI::Layout
{
public:
MainMenu(int w, int h);
int mWidth;
int mHeight;
public:
MainMenu(int w, int h);
~MainMenu();
void onResChange(int w, int h);
virtual void setVisible (bool visible);
private:
MyGUI::Widget* mButtonBox;
void onResChange(int w, int h);
std::map<std::string, MWGui::ImageButton*> mButtons;
private:
MyGUI::Widget* mButtonBox;
void onButtonClicked (MyGUI::Widget* sender);
std::map<std::string, MWGui::ImageButton*> mButtons;
void updateMenu();
void onButtonClicked (MyGUI::Widget* sender);
SaveGameDialog* mSaveGameDialog;
};
}

@ -373,8 +373,9 @@ namespace MWGui
// ------------------------------------------------------------------------------------------
MapWindow::MapWindow(const std::string& cacheDir)
: MWGui::WindowPinnableBase("openmw_map_window.layout")
MapWindow::MapWindow(DragAndDrop* drag, const std::string& cacheDir)
: WindowPinnableBase("openmw_map_window.layout")
, NoDrop(drag, mMainWidget)
, mGlobal(false)
, mGlobalMap(0)
, mGlobalMapRender(0)
@ -434,7 +435,7 @@ namespace MWGui
static int _counter=0;
MyGUI::Button* markerWidget = mGlobalMapImage->createWidget<MyGUI::Button>("ButtonImage",
MyGUI::Button* markerWidget = mGlobalMapOverlay->createWidget<MyGUI::Button>("ButtonImage",
widgetCoord, MyGUI::Align::Default, "Door" + boost::lexical_cast<std::string>(_counter));
markerWidget->setImageResource("DoorMarker");
markerWidget->setUserString("ToolTipType", "Layout");
@ -499,10 +500,11 @@ namespace MWGui
mGlobalMap->setCanvasSize (mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight());
mGlobalMapImage->setSize(mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight());
for (unsigned int i=0; i<mGlobalMapImage->getChildCount (); ++i)
// force markers to foreground
for (unsigned int i=0; i<mGlobalMapOverlay->getChildCount (); ++i)
{
if (mGlobalMapImage->getChildAt (i)->getName().substr(0,4) == "Door")
mGlobalMapImage->getChildAt (i)->castType<MyGUI::Button>()->setImageResource("DoorMarker");
if (mGlobalMapOverlay->getChildAt (i)->getName().substr(0,4) == "Door")
mGlobalMapOverlay->getChildAt (i)->castType<MyGUI::Button>()->setImageResource("DoorMarker");
}
globalMapUpdatePlayer();
@ -573,4 +575,31 @@ namespace MWGui
mGlobalMap->setViewOffset(viewoffs);
}
void MapWindow::clear()
{
mGlobalMapRender->clear();
while (mEventBoxGlobal->getChildCount())
MyGUI::Gui::getInstance().destroyWidget(mEventBoxGlobal->getChildAt(0));
while (mGlobalMapOverlay->getChildCount())
MyGUI::Gui::getInstance().destroyWidget(mGlobalMapOverlay->getChildAt(0));
}
void MapWindow::write(ESM::ESMWriter &writer)
{
mGlobalMapRender->write(writer);
}
void MapWindow::readRecord(ESM::ESMReader &reader, int32_t type)
{
std::vector<std::pair<int, int> > exploredCells;
mGlobalMapRender->readRecord(reader, type, exploredCells);
for (std::vector<std::pair<int, int> >::iterator it = exploredCells.begin(); it != exploredCells.end(); ++it)
{
const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Cell>().search(it->first, it->second);
if (cell && !cell->mName.empty())
addVisitedLocation(cell->mName, it->first, it->second);
}
}
}

@ -8,6 +8,12 @@ namespace MWRender
class GlobalMap;
}
namespace ESM
{
class ESMReader;
class ESMWriter;
}
namespace Loading
{
class Listener;
@ -75,10 +81,10 @@ namespace MWGui
float mLastDirectionY;
};
class MapWindow : public MWGui::WindowPinnableBase, public LocalMapBase
class MapWindow : public MWGui::WindowPinnableBase, public LocalMapBase, public NoDrop
{
public:
MapWindow(const std::string& cacheDir);
MapWindow(DragAndDrop* drag, const std::string& cacheDir);
virtual ~MapWindow();
void setCellName(const std::string& cellName);
@ -92,6 +98,14 @@ namespace MWGui
virtual void open();
void onFrame(float dt) { NoDrop::onFrame(dt); }
/// Clear all savegame-specific data
void clear();
void write (ESM::ESMWriter& writer);
void readRecord (ESM::ESMReader& reader, int32_t type);
private:
void onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);
void onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);

@ -287,24 +287,6 @@ namespace MWGui
else {
mainWidgetSize.width = textSize.width + 3*textPadding;
}
mainWidgetSize.height = textSize.height + 2*textPadding + textButtonPadding + buttonHeight * buttons.size() + buttonMainPadding;
mMainWidget->setSize(mainWidgetSize);
MyGUI::IntCoord absCoord;
absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2;
absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2;
mMainWidget->setCoord(absCoord);
mMainWidget->setSize(mainWidgetSize);
MyGUI::IntCoord messageWidgetCoord;
messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2;
messageWidgetCoord.top = textPadding;
mMessageWidget->setCoord(messageWidgetCoord);
mMessageWidget->setSize(textSize);
MyGUI::IntCoord buttonCord;
MyGUI::IntSize buttonSize(0, buttonHeight);
@ -326,6 +308,21 @@ namespace MWGui
top += buttonSize.height + 2*buttonTopPadding;
}
mainWidgetSize.height = top + buttonMainPadding;
mMainWidget->setSize(mainWidgetSize);
MyGUI::IntPoint absPos;
absPos.left = (gameWindowSize.width - mainWidgetSize.width)/2;
absPos.top = (gameWindowSize.height - mainWidgetSize.height)/2;
mMainWidget->setPosition(absPos);
MyGUI::IntCoord messageWidgetCoord;
messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2;
messageWidgetCoord.top = textPadding;
messageWidgetCoord.width = textSize.width;
messageWidgetCoord.height = textSize.height;
mMessageWidget->setCoord(messageWidgetCoord);
}
// Set key focus to "Ok" button
@ -335,7 +332,7 @@ namespace MWGui
{
if(Misc::StringUtils::ciEqual((*button)->getCaption(), ok))
{
MyGUI::InputManager::getInstance().setKeyFocusWidget(*button);
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(*button);
(*button)->eventKeyButtonPressed += MyGUI::newDelegate(this, &InteractiveMessageBox::onKeyPressed);
break;
}

@ -3,8 +3,15 @@
#include <boost/lexical_cast.hpp>
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/actionequip.hpp"
#include "../mwworld/class.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "../mwmechanics/spells.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwgui/inventorywindow.hpp"
#include "../mwgui/bookwindow.hpp"
#include "../mwgui/scrollwindow.hpp"
@ -17,8 +24,8 @@ namespace
{
bool sortItems(const MWWorld::Ptr& left, const MWWorld::Ptr& right)
{
int cmp = MWWorld::Class::get(left).getName(left).compare(
MWWorld::Class::get(right).getName(right));
int cmp = left.getClass().getName(left).compare(
right.getClass().getName(right));
return cmp < 0;
}
@ -334,10 +341,7 @@ namespace MWGui
// equip, if it can be equipped
if (!MWWorld::Class::get(item).getEquipmentSlots(item).first.empty())
{
// Note: can't use Class::use here because enchanted scrolls for example would then open the scroll window instead of equipping
MWWorld::ActionEquip action(item);
action.execute (MWBase::Environment::get().getWorld ()->getPlayerPtr());
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->useItem(item);
}
store.setSelectedEnchantItem(it);

@ -70,8 +70,7 @@ namespace MWGui
setText("RaceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu5", "Race"));
getWidget(mRaceList, "RaceList");
mRaceList->setScrollVisible(true);
mRaceList->eventListSelectAccept += MyGUI::newDelegate(this, &RaceDialog::onSelectRace);
mRaceList->eventListMouseItemActivate += MyGUI::newDelegate(this, &RaceDialog::onSelectRace);
mRaceList->eventListSelectAccept += MyGUI::newDelegate(this, &RaceDialog::onAccept);
mRaceList->eventListChangePosition += MyGUI::newDelegate(this, &RaceDialog::onSelectRace);
setText("SkillsT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sBonusSkillTitle", "Skill Bonus"));
@ -241,6 +240,14 @@ namespace MWGui
updateSpellPowers();
}
void RaceDialog::onAccept(MyGUI::ListBox *_sender, size_t _index)
{
onSelectRace(_sender, _index);
if(mRaceList->getIndexSelected() == MyGUI::ITEM_NONE)
return;
eventDone(this);
}
void RaceDialog::getBodyParts (int part, std::vector<std::string>& out)
{
out.clear();

@ -67,6 +67,7 @@ namespace MWGui
void onSelectNextHair(MyGUI::Widget* _sender);
void onSelectRace(MyGUI::ListBox* _sender, size_t _index);
void onAccept(MyGUI::ListBox* _sender, size_t _index);
void onOkClicked(MyGUI::Widget* _sender);
void onBackClicked(MyGUI::Widget* _sender);

@ -16,7 +16,7 @@ namespace MWGui
void ReferenceInterface::checkReferenceAvailable()
{
MWWorld::Ptr::CellStore* playerCell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell();
MWWorld::CellStore* playerCell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell();
// check if player has changed cell, or count of the reference has become 0
if ((playerCell != mCurrentPlayerCell && mCurrentPlayerCell != NULL)

@ -1,12 +1,28 @@
#include "savegamedialog.hpp"
#include "widgets.hpp"
#include <OgreImage.h>
#include <OgreTextureManager.h>
#include <components/misc/stringops.hpp>
#include <components/settings/settings.hpp>
#include "../mwbase/statemanager.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwstate/character.hpp"
#include "confirmationdialog.hpp"
namespace MWGui
{
SaveGameDialog::SaveGameDialog()
: WindowModal("openmw_savegame_dialog.layout")
, mSaving(true)
, mCurrentCharacter(NULL)
{
getWidget(mScreenshot, "Screenshot");
getWidget(mCharacterSelection, "SelectCharacter");
@ -18,21 +34,89 @@ namespace MWGui
getWidget(mSpacer, "Spacer");
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onOkButtonClicked);
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onCancelButtonClicked);
mCharacterSelection->eventComboChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected);
mSaveList->eventListChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onSlotSelected);
mSaveList->eventListSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onSlotActivated);
mSaveNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onEditSelectAccept);
mSaveNameEdit->eventEditTextChange += MyGUI::newDelegate(this, &SaveGameDialog::onSaveNameChanged);
}
void SaveGameDialog::onSlotActivated(MyGUI::ListBox *sender, size_t pos)
{
onSlotSelected(sender, pos);
accept();
}
void SaveGameDialog::onSaveNameChanged(MyGUI::EditBox *sender)
{
// This might have previously been a save slot from the list. If so, that is no longer the case
mSaveList->setIndexSelected(MyGUI::ITEM_NONE);
onSlotSelected(mSaveList, MyGUI::ITEM_NONE);
}
void SaveGameDialog::onEditSelectAccept(MyGUI::EditBox *sender)
{
accept();
}
void SaveGameDialog::open()
{
WindowModal::open();
mSaveNameEdit->setCaption ("");
if (mSaving)
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveNameEdit);
center();
MWBase::StateManager* mgr = MWBase::Environment::get().getStateManager();
if (mgr->characterBegin() == mgr->characterEnd())
return;
mCurrentCharacter = mgr->getCurrentCharacter (false);
std::string directory =
Misc::StringUtils::lowerCase (Settings::Manager::getString ("character", "Saves"));
mCharacterSelection->removeAllItems();
for (MWBase::StateManager::CharacterIterator it = mgr->characterBegin(); it != mgr->characterEnd(); ++it)
{
if (it->begin()!=it->end())
{
std::stringstream title;
title << it->getSignature().mPlayerName;
title << " (Level " << it->getSignature().mPlayerLevel << " " << it->getSignature().mPlayerClass << ")";
mCharacterSelection->addItem (title.str());
if (mCurrentCharacter == &*it ||
(!mCurrentCharacter && !mSaving && directory==Misc::StringUtils::lowerCase (
it->begin()->mPath.parent_path().filename().string())))
{
mCurrentCharacter = &*it;
mCharacterSelection->setIndexSelected(mCharacterSelection->getItemCount()-1);
}
}
}
fillSaveList();
}
void SaveGameDialog::setLoadOrSave(bool load)
{
mSaving = !load;
mSaveNameEdit->setVisible(!load);
mCharacterSelection->setUserString("Hidden", load ? "false" : "true");
mCharacterSelection->setVisible(load);
mSpacer->setUserString("Hidden", load ? "false" : "true");
if (!load)
{
mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter (false);
}
center();
}
@ -41,9 +125,161 @@ namespace MWGui
setVisible(false);
}
void SaveGameDialog::onOkButtonClicked(MyGUI::Widget *sender)
void SaveGameDialog::onConfirmationGiven()
{
accept(true);
}
void SaveGameDialog::accept(bool reallySure)
{
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(NULL);
// Get the selected slot, if any
unsigned int i=0;
const MWState::Slot* slot = NULL;
if (mCurrentCharacter)
{
for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it,++i)
{
if (i == mSaveList->getIndexSelected())
slot = &*it;
}
}
if (mSaving)
{
// If overwriting an existing slot, ask for confirmation first
if (slot != NULL && !reallySure)
{
ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog();
dialog->open("#{sMessage4}");
dialog->eventOkClicked.clear();
dialog->eventOkClicked += MyGUI::newDelegate(this, &SaveGameDialog::onConfirmationGiven);
dialog->eventCancelClicked.clear();
return;
}
if (mSaveNameEdit->getCaption().empty())
{
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage65}");
return;
}
MWBase::Environment::get().getStateManager()->saveGame (mSaveNameEdit->getCaption(), slot);
}
else
{
if (mCurrentCharacter && slot)
MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, slot);
}
setVisible(false);
if (MWBase::Environment::get().getStateManager()->getState()==
MWBase::StateManager::State_NoGame)
{
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
}
}
void SaveGameDialog::onOkButtonClicked(MyGUI::Widget *sender)
{
accept();
}
void SaveGameDialog::onCharacterSelected(MyGUI::ComboBox *sender, size_t pos)
{
MWBase::StateManager* mgr = MWBase::Environment::get().getStateManager();
unsigned int i=0;
const MWState::Character* character = NULL;
for (MWBase::StateManager::CharacterIterator it = mgr->characterBegin(); it != mgr->characterEnd(); ++it, ++i)
{
if (i == pos)
character = &*it;
}
assert(character && "Can't find selected character");
mCurrentCharacter = character;
fillSaveList();
}
void SaveGameDialog::fillSaveList()
{
mSaveList->removeAllItems();
if (!mCurrentCharacter)
return;
for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it)
{
mSaveList->addItem(it->mProfile.mDescription);
}
onSlotSelected(mSaveList, MyGUI::ITEM_NONE);
}
void SaveGameDialog::onSlotSelected(MyGUI::ListBox *sender, size_t pos)
{
if (pos == MyGUI::ITEM_NONE)
{
mInfoText->setCaption("");
mScreenshot->setImageTexture("");
return;
}
if (mSaving)
mSaveNameEdit->setCaption(sender->getItemNameAt(pos));
const MWState::Slot* slot = NULL;
unsigned int i=0;
for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it, ++i)
{
if (i == pos)
slot = &*it;
}
assert(slot && "Can't find selected slot");
std::stringstream text;
time_t time = slot->mTimeStamp;
struct tm* timeinfo;
timeinfo = localtime(&time);
text << asctime(timeinfo) << "\n";
text << "Level " << slot->mProfile.mPlayerLevel << "\n";
text << slot->mProfile.mPlayerCell << "\n";
// text << "Time played: " << slot->mProfile.mTimePlayed << "\n";
int hour = int(slot->mProfile.mInGameTime.mGameHour);
bool pm = hour >= 12;
if (hour >= 13) hour -= 12;
if (hour == 0) hour = 12;
text
<< slot->mProfile.mInGameTime.mDay << " "
<< MWBase::Environment::get().getWorld()->getMonthName(slot->mProfile.mInGameTime.mMonth)
<< " " << hour << " " << (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}");
mInfoText->setCaptionWithReplacing(text.str());
// Decode screenshot
std::vector<char> data = slot->mProfile.mScreenshot; // MemoryDataStream doesn't work with const data :(
Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&data[0], data.size()));
Ogre::Image image;
image.load(stream, "jpg");
const std::string textureName = "@savegame_screenshot";
Ogre::TexturePtr texture;
texture = Ogre::TextureManager::getSingleton().getByName(textureName);
mScreenshot->setImageTexture("");
if (texture.isNull())
{
texture = Ogre::TextureManager::getSingleton().createManual(textureName,
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
Ogre::TEX_TYPE_2D,
image.getWidth(), image.getHeight(), 0, Ogre::PF_BYTE_RGBA, Ogre::TU_DYNAMIC_WRITE_ONLY);
}
texture->unload();
texture->setWidth(image.getWidth());
texture->setHeight(image.getHeight());
texture->loadImage(image);
mScreenshot->setImageTexture(textureName);
}
}

@ -3,6 +3,11 @@
#include "windowbase.hpp"
namespace MWState
{
class Character;
}
namespace MWGui
{
@ -15,12 +20,22 @@ namespace MWGui
void setLoadOrSave(bool load);
private:
void onCancelButtonClicked (MyGUI::Widget* sender);
void onOkButtonClicked (MyGUI::Widget* sender);
void onCharacterSelected (MyGUI::ComboBox* sender, size_t pos);
void onSlotSelected (MyGUI::ListBox* sender, size_t pos);
void onSlotActivated (MyGUI::ListBox* sender, size_t pos);
void onEditSelectAccept (MyGUI::EditBox* sender);
void onSaveNameChanged (MyGUI::EditBox* sender);
void onConfirmationGiven();
void accept(bool reallySure=false);
void fillSaveList();
private:
MyGUI::ImageBox* mScreenshot;
bool mSaving;
MyGUI::ComboBox* mCharacterSelection;
MyGUI::EditBox* mInfoText;
@ -30,6 +45,8 @@ namespace MWGui
MyGUI::EditBox* mSaveNameEdit;
MyGUI::Widget* mSpacer;
const MWState::Character* mCurrentCharacter;
};
}

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

Loading…
Cancel
Save