merged with master

actorid
mrcheko 11 years ago
commit 3a5da7e6e8

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

@ -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)
{
@ -34,4 +36,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
{
@ -200,6 +211,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);
};
}

@ -3,6 +3,7 @@
#include <string>
#include <vector>
#include <list>
namespace Ogre
{
@ -159,6 +160,9 @@ namespace MWBase
virtual bool isAIActive() = 0;
virtual void getObjectsInRange (const Ogre::Vector3& position, float radius, std::vector<MWWorld::Ptr>& objects) = 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;
};
}

@ -28,6 +28,7 @@
#include "../mwbase/mechanicsmanager.hpp"
#include "aicombat.hpp"
#include "aifollow.hpp"
namespace
{
@ -795,7 +796,7 @@ namespace MWMechanics
}
// If it's the player and God Mode is turned on, keep it alive
if(iter->first.getRefData().getHandle()=="player" &&
if(iter->first.getRefData().getHandle()=="player" &&
MWBase::Environment::get().getWorld()->getGodModeState())
{
MWMechanics::DynamicStat<float> stat(stats.getHealth());
@ -928,4 +929,22 @@ namespace MWMechanics
out.push_back(iter->first);
}
}
std::list<MWWorld::Ptr> Actors::getActorsFollowing(const MWWorld::Ptr& actor)
{
std::list<MWWorld::Ptr> list;
for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();iter++)
{
const MWWorld::Class &cls = MWWorld::Class::get(iter->first);
CreatureStats &stats = cls.getCreatureStats(iter->first);
if(stats.getAiSequence().getTypeId() == AiPackage::TypeIdFollow)
{
MWMechanics::AiFollow* package = static_cast<MWMechanics::AiFollow*>(stats.getAiSequence().getActivePackage());
if(package->getFollowedActor() == actor.getCellRef().mRefID)
list.push_front(iter->first);
}
}
return list;
}
}

@ -84,7 +84,7 @@ namespace MWMechanics
int getHoursToRest(const MWWorld::Ptr& ptr) const;
///< Calculate how many hours the given actor needs to rest in order to be fully healed
int countDeaths (const std::string& id) const;
///< Return the number of deaths for actors with the given ID.
@ -94,7 +94,10 @@ namespace MWMechanics
void skipAnimation(const MWWorld::Ptr& ptr);
bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName);
void getObjectsInRange(const Ogre::Vector3& position, float radius, std::vector<MWWorld::Ptr>& out);
void getObjectsInRange(const Ogre::Vector3& position, float radius, std::vector<MWWorld::Ptr>& out);
std::list<MWWorld::Ptr> getActorsFollowing(const MWWorld::Ptr& actor);
///<return the list of actors which are following the given actor (ie AiFollow is active and the target is the actor)
private:
PtrControllerMap mActors;

@ -208,7 +208,7 @@ namespace MWMechanics
mMovement.mPosition[1] = 0;
chooseBestAttack(weapon, mMovement);
if(mMovement.mPosition[0] != 0 || mMovement.mPosition[1])
if(mMovement.mPosition[0] || mMovement.mPosition[1])
{
mTimerCombatMove = 0.1f + 0.1f * static_cast<float>(rand())/RAND_MAX;
mCombatMove = true;
@ -258,7 +258,8 @@ namespace MWMechanics
// TODO: use movement settings instead of rotating directly
MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false);
//MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
//mMovement.mRotation[2] = 10*(Ogre::Degree(zAngle).valueRadians()-pos.rot[2]);
mMovement.mPosition[1] = 1;
mReadyToAttack = false;
}
@ -280,7 +281,7 @@ namespace MWMechanics
float s1 = distBetween - weapRange;
float t = s1/speed1;
float s2 = speed2 * t;
float t_swing = 0.17f/weapSpeed;//0.17 should be the time of playing weapon anim from 'start' to 'hit' tags
float t_swing = 0.17f/weapSpeed;//instead of 0.17 should be the time of playing weapon anim from 'start' to 'hit' tags
if (t + s2/speed1 <= t_swing)
{
mReadyToAttack = true;
@ -326,24 +327,12 @@ namespace MWMechanics
start.mY = pos.pos[1];
start.mZ = pos.pos[2];
const ESM::Pathgrid *pathgrid =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*actor.getCell()->mCell);
float xCell = 0;
float yCell = 0;
if (actor.getCell()->mCell->isExterior())
{
xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE;
yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE;
}
if(!mPathFinder.isPathConstructed())
mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, isOutside);
mPathFinder.buildPath(start, dest, actor.getCell(), isOutside);
else
{
PathFinder newPathFinder;
newPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, isOutside);
newPathFinder.buildPath(start, dest, actor.getCell(), isOutside);
//TO EXPLORE:
//maybe here is a mistake (?): PathFinder::getPathSize() returns number of grid points in the path,
@ -421,4 +410,4 @@ void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement
movement.mPosition[1] = movement.mPosition[0] = 0;
}
}
}

@ -28,8 +28,8 @@ namespace MWMechanics
{
AiEscort::AiEscort(const std::string &actorId, int duration, float x, float y, float z)
: mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration)
, cellX(std::numeric_limits<int>::max())
, cellY(std::numeric_limits<int>::max())
, mCellX(std::numeric_limits<int>::max())
, mCellY(std::numeric_limits<int>::max())
{
mMaxDist = 470;
@ -47,8 +47,8 @@ namespace MWMechanics
AiEscort::AiEscort(const std::string &actorId, const std::string &cellId,int duration, float x, float y, float z)
: mActorId(actorId), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration)
, cellX(std::numeric_limits<int>::max())
, cellY(std::numeric_limits<int>::max())
, mCellX(std::numeric_limits<int>::max())
, mCellY(std::numeric_limits<int>::max())
{
mMaxDist = 470;
@ -84,9 +84,7 @@ namespace MWMechanics
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
ESM::Position pos = actor.getRefData().getPosition();
bool cellChange = actor.getCell()->mCell->mData.mX != cellX || actor.getCell()->mCell->mData.mY != cellY;
const ESM::Pathgrid *pathgrid =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*actor.getCell()->mCell);
bool cellChange = actor.getCell()->mCell->mData.mX != mCellX || actor.getCell()->mCell->mData.mY != mCellY;
if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX)
{
@ -116,15 +114,8 @@ namespace MWMechanics
if(!mPathFinder.isPathConstructed() || cellChange)
{
cellX = actor.getCell()->mCell->mData.mX;
cellY = actor.getCell()->mCell->mData.mY;
float xCell = 0;
float yCell = 0;
if (actor.getCell()->mCell->isExterior())
{
xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE;
yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE;
}
mCellX = actor.getCell()->mCell->mData.mX;
mCellY = actor.getCell()->mCell->mData.mY;
ESM::Pathgrid::Point dest;
dest.mX = mX;
@ -136,7 +127,7 @@ namespace MWMechanics
start.mY = pos.pos[1];
start.mZ = pos.pos[2];
mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, true);
mPathFinder.buildPath(start, dest, actor.getCell(), true);
}
if(mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]))

@ -34,8 +34,8 @@ namespace MWMechanics
unsigned int mDuration;
PathFinder mPathFinder;
int cellX;
int cellY;
int mCellX;
int mCellY;
};
}
#endif

@ -1,27 +1,120 @@
#include "aifollow.hpp"
#include <iostream>
#include "aifollow.hpp"
#include <iostream>
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwworld/class.hpp"
#include "movement.hpp"
#include <OgreMath.h>
MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z)
: mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId)
MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z)
: mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(""), mTimer(0), mStuckTimer(0)
{
}
MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z)
: mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId)
: mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId), mTimer(0), mStuckTimer(0)
{
}
bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration)
{
const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mActorId, false);
mTimer = mTimer + duration;
mStuckTimer = mStuckTimer + duration;
mTotalTime = mTotalTime + duration;
ESM::Position pos = actor.getRefData().getPosition();
if(mTotalTime > mDuration && mDuration != 0)
return true;
if((pos.pos[0]-mX)*(pos.pos[0]-mX) +
(pos.pos[1]-mY)*(pos.pos[1]-mY) +
(pos.pos[2]-mZ)*(pos.pos[2]-mZ) < 100*100)
{
if(actor.getCell()->isExterior())
{
if(mCellId == "")
return true;
}
else
{
if(mCellId == actor.getCell()->mCell->mName)
return true;
}
}
ESM::Pathgrid::Point dest;
dest.mX = target.getRefData().getPosition().pos[0];
dest.mY = target.getRefData().getPosition().pos[1];
dest.mZ = target.getRefData().getPosition().pos[2];
ESM::Pathgrid::Point start;
start.mX = pos.pos[0];
start.mY = pos.pos[1];
start.mZ = pos.pos[2];
if(mPathFinder.getPath().empty())
mPathFinder.buildPath(start, dest, actor.getCell(), true);
if(mTimer > 0.25)
{
if(!mPathFinder.getPath().empty())
{
ESM::Pathgrid::Point lastPos = mPathFinder.getPath().back();
if((dest.mX - lastPos.mX)*(dest.mX - lastPos.mX)
+(dest.mY - lastPos.mY)*(dest.mY - lastPos.mY)
+(dest.mZ - lastPos.mZ)*(dest.mZ - lastPos.mZ)
> 100*100)
mPathFinder.addPointToPath(dest);
}
mTimer = 0;
}
if(mStuckTimer>0.5)
{
if((mStuckPos.pos[0] - pos.pos[0])*(mStuckPos.pos[0] - pos.pos[0])
+(mStuckPos.pos[1] - pos.pos[1])*(mStuckPos.pos[1] - pos.pos[1])
+(mStuckPos.pos[2] - pos.pos[2])*(mStuckPos.pos[2] - pos.pos[2]) < 100) //NPC is stuck
mPathFinder.buildPath(start, dest, actor.getCell(), true);
mStuckTimer = 0;
mStuckPos = pos;
}
if(!mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]))
{
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
//MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false);
MWWorld::Class::get(actor).getMovementSettings(actor).mRotation[2] = 10*(Ogre::Degree(zAngle).valueRadians()-pos.rot[2]);
//std::cout << Ogre::Degree(zAngle).valueDegrees()-Ogre::Radian(actor.getRefData().getPosition().rot[2]).valueDegrees() << " "<< pos.rot[2] << " " << zAngle << "\n";
//MWWorld::Class::get(actor).get
}
if((dest.mX - pos.pos[0])*(dest.mX - pos.pos[0])+(dest.mY - pos.pos[1])*(dest.mY - pos.pos[1])+(dest.mZ - pos.pos[2])*(dest.mZ - pos.pos[2])
< 100*100)
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
else
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
return false;
}
std::string MWMechanics::AiFollow::getFollowedActor()
{
return mActorId;
}
MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const
{
return new AiFollow(*this);
}
bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration)
{
std::cout << "AiFollow completed.\n";
return true;
}
int MWMechanics::AiFollow::getTypeId() const
{
return TypeIdFollow;
}
}

@ -3,6 +3,8 @@
#include "aipackage.hpp"
#include <string>
#include "pathfinding.hpp"
#include "../../../components/esm/defs.hpp"
namespace MWMechanics
{
@ -17,6 +19,8 @@ namespace MWMechanics
///< \return Package completed?
virtual int getTypeId() const;
std::string getFollowedActor();
private:
float mDuration;
float mX;
@ -24,6 +28,14 @@ namespace MWMechanics
float mZ;
std::string mActorId;
std::string mCellId;
float mTimer;
float mStuckTimer;
float mTotalTime;
ESM::Position mStuckPos;
PathFinder mPathFinder;
};
}
#endif

@ -124,6 +124,14 @@ void MWMechanics::AiSequence::queue (const AiPackage& package)
mPackages.push_back (package.clone());
}
MWMechanics::AiPackage* MWMechanics::AiSequence::getActivePackage()
{
if(mPackages.empty())
throw std::runtime_error(std::string("No AI Package!"));
else
return mPackages.front();
}
void MWMechanics::AiSequence::fill(const ESM::AIPackageList &list)
{
for (std::vector<ESM::AIPackage>::const_iterator it = list.mList.begin(); it != list.mList.end(); ++it)

@ -59,6 +59,9 @@ namespace MWMechanics
///< Add \a package to the end of the sequence (executed after all other packages have been
/// completed)
AiPackage* getActivePackage();
///< return the current active package. If there is no active package, throw an exeption
void fill (const ESM::AIPackageList& list);
};
}

@ -61,20 +61,11 @@ namespace MWMechanics
}
}
const ESM::Pathgrid *pathgrid = world->getStore().get<ESM::Pathgrid>().search(*cell);
bool cellChange = cell->mData.mX != cellX || cell->mData.mY != cellY;
if(!mPathFinder.isPathConstructed() || cellChange)
{
cellX = cell->mData.mX;
cellY = cell->mData.mY;
float xCell = 0;
float yCell = 0;
if(cell->isExterior())
{
xCell = cell->mData.mX * ESM::Land::REAL_SIZE;
yCell = cell->mData.mY * ESM::Land::REAL_SIZE;
}
ESM::Pathgrid::Point dest;
dest.mX = mX;
@ -86,7 +77,7 @@ namespace MWMechanics
start.mY = pos.pos[1];
start.mZ = pos.pos[2];
mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, true);
mPathFinder.buildPath(start, dest, actor.getCell(), true);
}
if(mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2]))

@ -260,7 +260,7 @@ namespace MWMechanics
start.mY = pos.pos[1];
start.mZ = pos.pos[2];
mPathFinder.buildPath(start, dest, mPathgrid, mXCell, mYCell, false);
mPathFinder.buildPath(start, dest, actor.getCell(), false);
if(mPathFinder.isPathConstructed())
{

@ -956,4 +956,9 @@ namespace MWMechanics
mActors.getObjectsInRange(position, radius, objects);
mObjects.getObjectsInRange(position, radius, objects);
}
std::list<MWWorld::Ptr> MechanicsManager::getActorsFollowing(const MWWorld::Ptr& actor)
{
return mActors.getActorsFollowing(actor);
}
}

@ -136,7 +136,9 @@ namespace MWMechanics
virtual void toggleAI();
virtual bool isAIActive();
virtual void getObjectsInRange (const Ogre::Vector3& position, float radius, std::vector<MWWorld::Ptr>& objects);
virtual void getObjectsInRange (const Ogre::Vector3& position, float radius, std::vector<MWWorld::Ptr>& objects);
virtual std::list<MWWorld::Ptr> getActorsFollowing(const MWWorld::Ptr& actor);
};
}

@ -7,30 +7,10 @@
#include "OgreVector3.h"
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <map>
namespace
{
struct found_path {};
typedef boost::adjacency_list< boost::vecS, boost::vecS, boost::undirectedS,
boost::property<boost::vertex_index_t, int, ESM::Pathgrid::Point>, boost::property<boost::edge_weight_t, float> >
PathGridGraph;
typedef boost::property_map<PathGridGraph, boost::edge_weight_t>::type WeightMap;
typedef PathGridGraph::vertex_descriptor PointID;
typedef PathGridGraph::edge_descriptor PointConnectionID;
class goalVisited : public boost::default_dijkstra_visitor
{
public:
goalVisited(PointID goal) {mGoal = goal;};
void examine_vertex(PointID u, const PathGridGraph g) {if(u == mGoal) throw found_path();};
private:
PointID mGoal;
};
float distanceZCorrected(ESM::Pathgrid::Point point, float x, float y, float z)
{
x -= point.mX;
@ -82,64 +62,104 @@ namespace
return closestIndex;
}
PathGridGraph buildGraph(const ESM::Pathgrid* pathgrid, float xCell = 0, float yCell = 0)
/*std::list<ESM::Pathgrid::Point> reconstructPath(const std::vector<MWMechanics::PathFinder::Node>& graph,const ESM::Pathgrid* pathgrid, int lastNode,float xCell, float yCell)
{
PathGridGraph graph;
for(unsigned int counter = 0; counter < pathgrid->mPoints.size(); counter++)
std::list<ESM::Pathgrid::Point> path;
while(graph[lastNode].parent != -1)
{
PointID pID = boost::add_vertex(graph);
graph[pID].mX = pathgrid->mPoints[counter].mX + xCell;
graph[pID].mY = pathgrid->mPoints[counter].mY + yCell;
graph[pID].mZ = pathgrid->mPoints[counter].mZ;
//std::cout << "not empty" << xCell;
ESM::Pathgrid::Point pt = pathgrid->mPoints[lastNode];
pt.mX += xCell;
pt.mY += yCell;
path.push_front(pt);
lastNode = graph[lastNode].parent;
}
return path;
}*/
for(unsigned int counterTwo = 0; counterTwo < pathgrid->mEdges.size(); counterTwo++)
{
PointID u = pathgrid->mEdges[counterTwo].mV0;
PointID v = pathgrid->mEdges[counterTwo].mV1;
PointConnectionID edge;
bool done;
boost::tie(edge, done) = boost::add_edge(u, v, graph);
WeightMap weightmap = boost::get(boost::edge_weight, graph);
weightmap[edge] = distance(graph[u], graph[v]);
}
return graph;
}
std::list<ESM::Pathgrid::Point> findPath(PointID start, PointID end, PathGridGraph graph)
/*std::list<ESM::Pathgrid::Point> buildPath2(const ESM::Pathgrid* pathgrid,int start,int goal,float xCell = 0, float yCell = 0)
{
std::vector<PointID> p(boost::num_vertices(graph));
std::vector<float> d(boost::num_vertices(graph));
std::list<ESM::Pathgrid::Point> shortest_path;
try
std::vector<Node> graph;
for(unsigned int i = 0; i < pathgrid->mPoints.size(); i++)
{
Node node;
node.label = i;
node.parent = -1;
graph.push_back(node);
}
for(unsigned int i = 0; i < pathgrid->mEdges.size(); i++)
{
boost::dijkstra_shortest_paths(graph, start,
boost::predecessor_map(&p[0]).distance_map(&d[0]).visitor(goalVisited(end)));
Edge edge;
edge.destination = pathgrid->mEdges[i].mV1;
edge.cost = distance(pathgrid->mPoints[pathgrid->mEdges[i].mV0],pathgrid->mPoints[pathgrid->mEdges[i].mV1]);
graph[pathgrid->mEdges[i].mV0].edges.push_back(edge);
edge.destination = pathgrid->mEdges[i].mV0;
graph[pathgrid->mEdges[i].mV1].edges.push_back(edge);
}
catch(found_path& fg)
std::vector<float> g_score(pathgrid->mPoints.size(),-1.);
std::vector<float> f_score(pathgrid->mPoints.size(),-1.);
g_score[start] = 0;
f_score[start] = distance(pathgrid->mPoints[start],pathgrid->mPoints[goal]);
std::list<int> openset;
std::list<int> closedset;
openset.push_back(start);
int current = -1;
while(!openset.empty())
{
for(PointID v = end; ; v = p[v])
current = openset.front();
openset.pop_front();
if(current == goal) break;
closedset.push_back(current);
for(int j = 0;j<graph[current].edges.size();j++)
{
shortest_path.push_front(graph[v]);
if(p[v] == v)
break;
//int next = graph[current].edges[j].destination
if(std::find(closedset.begin(),closedset.end(),graph[current].edges[j].destination) == closedset.end())
{
int dest = graph[current].edges[j].destination;
float tentative_g = g_score[current] + graph[current].edges[j].cost;
bool isInOpenSet = std::find(openset.begin(),openset.end(),dest) != openset.end();
if(!isInOpenSet
|| tentative_g < g_score[dest] )
{
graph[dest].parent = current;
g_score[dest] = tentative_g;
f_score[dest] = tentative_g + distance(pathgrid->mPoints[dest],pathgrid->mPoints[goal]);
if(!isInOpenSet)
{
std::list<int>::iterator it = openset.begin();
for(it = openset.begin();it!= openset.end();it++)
{
if(g_score[*it]>g_score[dest])
break;
}
openset.insert(it,dest);
}
}
}
}
}
return reconstructPath(graph,pathgrid,current,xCell,yCell);
}*/
return shortest_path;
}
}
namespace MWMechanics
{
PathFinder::PathFinder()
:mIsPathConstructed(false),mIsGraphConstructed(false)
{
mIsPathConstructed = false;
}
void PathFinder::clearPath()
@ -149,10 +169,124 @@ namespace MWMechanics
mIsPathConstructed = false;
}
void PathFinder::buildPathgridGraph(const ESM::Pathgrid* pathGrid)
{
mGraph.clear();
mGScore.resize(pathGrid->mPoints.size(),-1);
mFScore.resize(pathGrid->mPoints.size(),-1);
Node defaultNode;
defaultNode.label = -1;
defaultNode.parent = -1;
mGraph.resize(pathGrid->mPoints.size(),defaultNode);
for(unsigned int i = 0; i < pathGrid->mPoints.size(); i++)
{
Node node;
node.label = i;
node.parent = -1;
mGraph[i] = node;
}
for(unsigned int i = 0; i < pathGrid->mEdges.size(); i++)
{
Edge edge;
edge.destination = pathGrid->mEdges[i].mV1;
edge.cost = distance(pathGrid->mPoints[pathGrid->mEdges[i].mV0],pathGrid->mPoints[pathGrid->mEdges[i].mV1]);
mGraph[pathGrid->mEdges[i].mV0].edges.push_back(edge);
edge.destination = pathGrid->mEdges[i].mV0;
mGraph[pathGrid->mEdges[i].mV1].edges.push_back(edge);
}
mIsGraphConstructed = true;
}
void PathFinder::cleanUpAStar()
{
for(int i=0;i<static_cast<int> (mGraph.size());i++)
{
mGraph[i].parent = -1;
mGScore[i] = -1;
mFScore[i] = -1;
}
}
std::list<ESM::Pathgrid::Point> PathFinder::aStarSearch(const ESM::Pathgrid* pathGrid,int start,int goal,float xCell, float yCell)
{
cleanUpAStar();
mGScore[start] = 0;
mFScore[start] = distance(pathGrid->mPoints[start],pathGrid->mPoints[goal]);
std::list<int> openset;
std::list<int> closedset;
openset.push_back(start);
int current = -1;
while(!openset.empty())
{
current = openset.front();
openset.pop_front();
if(current == goal) break;
closedset.push_back(current);
for(int j = 0;j<static_cast<int> (mGraph[current].edges.size());j++)
{
//int next = mGraph[current].edges[j].destination
if(std::find(closedset.begin(),closedset.end(),mGraph[current].edges[j].destination) == closedset.end())
{
int dest = mGraph[current].edges[j].destination;
float tentative_g = mGScore[current] + mGraph[current].edges[j].cost;
bool isInOpenSet = std::find(openset.begin(),openset.end(),dest) != openset.end();
if(!isInOpenSet
|| tentative_g < mGScore[dest] )
{
mGraph[dest].parent = current;
mGScore[dest] = tentative_g;
mFScore[dest] = tentative_g + distance(pathGrid->mPoints[dest],pathGrid->mPoints[goal]);
if(!isInOpenSet)
{
std::list<int>::iterator it = openset.begin();
for(it = openset.begin();it!= openset.end();it++)
{
if(mGScore[*it]>mGScore[dest])
break;
}
openset.insert(it,dest);
}
}
}
}
}
std::list<ESM::Pathgrid::Point> path;
while(mGraph[current].parent != -1)
{
//std::cout << "not empty" << xCell;
ESM::Pathgrid::Point pt = pathGrid->mPoints[current];
pt.mX += xCell;
pt.mY += yCell;
path.push_front(pt);
current = mGraph[current].parent;
}
if(path.empty())
{
ESM::Pathgrid::Point pt = pathGrid->mPoints[goal];
pt.mX += xCell;
pt.mY += yCell;
path.push_front(pt);
}
return path;
}
void PathFinder::buildPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint,
const ESM::Pathgrid *pathGrid, float xCell, float yCell, bool allowShortcuts)
const MWWorld::CellStore* cell, bool allowShortcuts)
{
mPath.clear();
if(mCell != cell) mIsGraphConstructed = false;
mCell = cell;
if(allowShortcuts)
{
if(MWBase::Environment::get().getWorld()->castRay(startPoint.mX, startPoint.mY, startPoint.mZ,
@ -162,13 +296,24 @@ namespace MWMechanics
if(!allowShortcuts)
{
const ESM::Pathgrid *pathGrid =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*mCell->mCell);
float xCell = 0;
float yCell = 0;
if (mCell->isExterior())
{
xCell = mCell->mCell->mData.mX * ESM::Land::REAL_SIZE;
yCell = mCell->mCell->mData.mY * ESM::Land::REAL_SIZE;
}
int startNode = getClosestPoint(pathGrid, startPoint.mX - xCell, startPoint.mY - yCell,startPoint.mZ);
int endNode = getClosestPoint(pathGrid, endPoint.mX - xCell, endPoint.mY - yCell, endPoint.mZ);
if(startNode != -1 && endNode != -1)
{
PathGridGraph graph = buildGraph(pathGrid, xCell, yCell);
mPath = findPath(startNode, endNode, graph);
if(!mIsGraphConstructed) buildPathgridGraph(pathGrid);
mPath = aStarSearch(pathGrid,startNode,endNode,xCell,yCell);//findPath(startNode, endNode, mGraph);
if(!mPath.empty())
{

@ -3,6 +3,12 @@
#include <components/esm/loadpgrd.hpp>
#include <list>
#include <boost/graph/adjacency_list.hpp>
namespace MWWorld
{
class CellStore;
}
namespace MWMechanics
{
@ -12,9 +18,11 @@ namespace MWMechanics
PathFinder();
void clearPath();
void buildPathgridGraph(const ESM::Pathgrid* pathGrid);
void buildPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint,
const ESM::Pathgrid* pathGrid, float xCell = 0, float yCell = 0,
bool allowShortcuts = true);
const MWWorld::CellStore* cell, bool allowShortcuts = true);
bool checkPathCompleted(float x, float y, float z);
///< \Returns true if the last point of the path has been reached.
@ -44,9 +52,38 @@ namespace MWMechanics
//This functions deletes that point.
void syncStart(const std::list<ESM::Pathgrid::Point> &path);
void addPointToPath(ESM::Pathgrid::Point &point)
{
mPath.push_back(point);
}
private:
std::list<ESM::Pathgrid::Point> mPath;
struct Edge
{
int destination;
float cost;
};
struct Node
{
int label;
std::vector<Edge> edges;
int parent;//used in pathfinding
};
std::vector<float> mGScore;
std::vector<float> mFScore;
std::list<ESM::Pathgrid::Point> aStarSearch(const ESM::Pathgrid* pathGrid,int start,int goal,float xCell = 0, float yCell = 0);
void cleanUpAStar();
std::vector<Node> mGraph;
bool mIsPathConstructed;
std::list<ESM::Pathgrid::Point> mPath;
bool mIsGraphConstructed;
const MWWorld::CellStore* mCell;
};
}

@ -3,6 +3,7 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "player.hpp"
namespace MWWorld
@ -16,11 +17,35 @@ namespace MWWorld
void ActionTeleport::executeImp (const Ptr& actor)
{
MWBase::World* world = MWBase::Environment::get().getWorld();
world->getPlayer().setTeleported(true);
if (mCellName.empty())
world->changeToExteriorCell (mPosition);
//find any NPC that is following the actor and teleport him too
std::list<MWWorld::Ptr> followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(actor);
for(std::list<MWWorld::Ptr>::iterator it = followers.begin();it != followers.end();it++)
{
std::cout << "teleporting someone!" << (*it).getCellRef().mRefID;
executeImp(*it);
}
if(actor == world->getPlayerPtr())
{
world->getPlayer().setTeleported(true);
if (mCellName.empty())
world->changeToExteriorCell (mPosition);
else
world->changeToInteriorCell (mCellName, mPosition);
}
else
world->changeToInteriorCell (mCellName, mPosition);
{
if (mCellName.empty())
{
int cellX;
int cellY;
world->positionToIndex(mPosition.pos[0],mPosition.pos[1],cellX,cellY);
world->moveObject(actor,*world->getExterior(cellX,cellY),
mPosition.pos[0],mPosition.pos[1],mPosition.pos[2]);
}
else
world->moveObject(actor,*world->getInterior(mCellName),mPosition.pos[0],mPosition.pos[1],mPosition.pos[2]);
}
}
}

Loading…
Cancel
Save