forked from teamnwah/openmw-tes3coop
merged with master
This commit is contained in:
commit
3a5da7e6e8
47 changed files with 810 additions and 159 deletions
|
@ -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();
|
||||
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 (
|
||||
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++)
|
||||
{
|
||||
boost::dijkstra_shortest_paths(graph, start,
|
||||
boost::predecessor_map(&p[0]).distance_map(&d[0]).visitor(goalVisited(end)));
|
||||
Node node;
|
||||
node.label = i;
|
||||
node.parent = -1;
|
||||
graph.push_back(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]);
|
||||
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 shortest_path;
|
||||
}
|
||||
}
|
||||
return reconstructPath(graph,pathgrid,current,xCell,yCell);
|
||||
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
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…
Reference in a new issue