diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index fca16ec0b..92a0d8f8d 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -192,9 +192,9 @@ namespace CSMWorld { ColumnId_NpcDestinations, "Destinations" }, { ColumnId_DestinationCell, "Cell"}, - { ColumnId_PosX, "X"}, - { ColumnId_PosY, "Y"}, - { ColumnId_PosZ, "Z"}, + { ColumnId_PosX, "Dest X"}, + { ColumnId_PosY, "Dest Y"}, + { ColumnId_PosZ, "Dest Z"}, { ColumnId_RotX, "Rotation X"}, { ColumnId_RotY, "Rotation Y"}, { ColumnId_RotZ, "Rotation Z"}, @@ -247,6 +247,17 @@ namespace CSMWorld { ColumnId_EffectRange, "Range"}, { ColumnId_EffectArea, "Area"}, + { ColumnId_AiPackageList, "Ai Packages"}, + { ColumnId_AiPackage, "Package"}, + { ColumnId_AiWanderDist, "Wander Dist"}, + { ColumnId_AiWanderDuration, "Wander Duration"}, + { ColumnId_AiWanderToD, "Wander ToD"}, + { ColumnId_AiWanderIdle, "Wander Idle"}, + { ColumnId_AiWanderRepeat, "Wander Repeat"}, + { ColumnId_AiActivateName, "Activate"}, + { ColumnId_AiTargetId, "Target ID"}, + { ColumnId_AiTargetCell, "Target Cell"}, + { ColumnId_UseValue1, "Use value 1" }, { ColumnId_UseValue2, "Use value 2" }, { ColumnId_UseValue3, "Use value 3" }, diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 4f77ee23a..2788966a7 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -183,9 +183,9 @@ namespace CSMWorld ColumnId_SpellId = 168, ColumnId_NpcDestinations = 169, ColumnId_DestinationCell = 170, - ColumnId_PosX = 171, - ColumnId_PosY = 172, - ColumnId_PosZ = 173, + ColumnId_PosX = 171, // these are float + ColumnId_PosY = 172, // these are float + ColumnId_PosZ = 173, // these are float ColumnId_RotX = 174, ColumnId_RotY = 175, ColumnId_RotZ = 176, @@ -214,9 +214,9 @@ namespace CSMWorld ColumnId_PathgridPoints = 199, ColumnId_PathgridIndex = 200, - ColumnId_PathgridPosX = 201, - ColumnId_PathgridPosY = 202, - ColumnId_PathgridPosZ = 203, + ColumnId_PathgridPosX = 201, // these are int + ColumnId_PathgridPosY = 202, // these are int + ColumnId_PathgridPosZ = 203, // these are int ColumnId_PathgridEdges = 204, ColumnId_PathgridEdgeIndex = 205, ColumnId_PathgridEdge0 = 206, @@ -236,6 +236,18 @@ namespace CSMWorld ColumnId_EffectRange = 217, ColumnId_EffectArea = 218, + ColumnId_AiPackageList = 219, + ColumnId_AiPackage = 220, + ColumnId_AiWanderDist = 221, + ColumnId_AiWanderDuration = 222, + ColumnId_AiWanderToD = 223, + ColumnId_AiWanderIdle = 224, + ColumnId_AiWanderRepeat = 225, + ColumnId_AiActivateName = 226, + // use ColumnId_PosX, etc for AI destinations + ColumnId_AiTargetId = 227, + ColumnId_AiTargetCell = 228, + // Allocated to a separate value range, so we don't get a collision should we ever need // to extend the number of use values. ColumnId_UseValue1 = 0x10000, diff --git a/apps/opencs/model/world/nestedcoladapterimp.cpp b/apps/opencs/model/world/nestedcoladapterimp.cpp index 785817d33..d974a34c7 100644 --- a/apps/opencs/model/world/nestedcoladapterimp.cpp +++ b/apps/opencs/model/world/nestedcoladapterimp.cpp @@ -121,7 +121,7 @@ namespace CSMWorld ESM::Pathgrid::Point point = pathgrid.mPoints[subRowIndex]; switch (subColIndex) { - case 0: break; + case 0: return; // return without saving case 1: point.mX = value.toInt(); break; case 2: point.mY = value.toInt(); break; case 3: point.mZ = value.toInt(); break; @@ -228,7 +228,7 @@ namespace CSMWorld ESM::Pathgrid::Edge edge = pathgrid.mEdges[subRowIndex]; switch (subColIndex) { - case 0: break; + case 0: return; // return without saving case 1: edge.mV0 = value.toInt(); break; case 2: edge.mV1 = value.toInt(); break; default: throw std::runtime_error("Pathgrid edge subcolumn index out of range"); diff --git a/apps/opencs/model/world/nestedcoladapterimp.hpp b/apps/opencs/model/world/nestedcoladapterimp.hpp index 387b07e5c..3328396fb 100644 --- a/apps/opencs/model/world/nestedcoladapterimp.hpp +++ b/apps/opencs/model/world/nestedcoladapterimp.hpp @@ -457,7 +457,8 @@ namespace CSMWorld effect.mRange = ESM::RT_Touch; else if (effectId == "Target") effect.mRange = ESM::RT_Target; - // else leave unchanged + else + return; // leave unchanged break; } case 4: effect.mArea = value.toInt(); break; diff --git a/apps/opencs/model/world/refidadapterimp.hpp b/apps/opencs/model/world/refidadapterimp.hpp index a3007d415..c0fcba43c 100644 --- a/apps/opencs/model/world/refidadapterimp.hpp +++ b/apps/opencs/model/world/refidadapterimp.hpp @@ -10,8 +10,6 @@ #include #include #include -#include -#include #include "record.hpp" #include "refiddata.hpp" @@ -489,6 +487,7 @@ namespace CSMWorld const RefIdColumn *mInventory; const RefIdColumn *mSpells; const RefIdColumn *mDestinations; + const RefIdColumn *mAiPackages; std::map mServices; ActorColumns (const NameColumns& base) : NameColumns (base) {} @@ -549,6 +548,9 @@ namespace CSMWorld if (column==mActors.mDestinations) return true; // to show nested tables in dialogue subview, see IdTree::hasChildren() + if (column==mActors.mAiPackages) + return true; // to show nested tables in dialogue subview, see IdTree::hasChildren() + std::map::const_iterator iter = mActors.mServices.find (column); @@ -985,7 +987,7 @@ namespace CSMWorld ESXRecordT container = record.get(); container.mInventory.mList = - static_cast >&>(nestedTable).mNestedTable; + static_cast >&>(nestedTable).mNestedTable; record.setModified (container); } @@ -997,7 +999,7 @@ namespace CSMWorld static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); // deleted by dtor of NestedTableStoring - return new NestedTableWrapper >(record.get().mInventory.mList); + return new NestedTableWrapper >(record.get().mInventory.mList); } virtual QVariant getNestedData (const RefIdColumn *column, @@ -1123,7 +1125,7 @@ namespace CSMWorld ESXRecordT caster = record.get(); caster.mSpells.mList = - static_cast >&>(nestedTable).mNestedTable; + static_cast >&>(nestedTable).mNestedTable; record.setModified (caster); } @@ -1135,7 +1137,7 @@ namespace CSMWorld static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); // deleted by dtor of NestedTableStoring - return new NestedTableWrapper >(record.get().mSpells.mList); + return new NestedTableWrapper >(record.get().mSpells.mList); } virtual QVariant getNestedData (const RefIdColumn *column, @@ -1258,7 +1260,7 @@ namespace CSMWorld ESXRecordT traveller = record.get(); traveller.mTransport.mList = - static_cast >&>(nestedTable).mNestedTable; + static_cast >&>(nestedTable).mNestedTable; record.setModified (traveller); } @@ -1270,7 +1272,7 @@ namespace CSMWorld static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); // deleted by dtor of NestedTableStoring - return new NestedTableWrapper >(record.get().mTransport.mList); + return new NestedTableWrapper >(record.get().mTransport.mList); } virtual QVariant getNestedData (const RefIdColumn *column, @@ -1375,6 +1377,304 @@ namespace CSMWorld return static_cast(record.get().mTransport.mList.size()); } }; + + template + class ActorAiRefIdAdapter : public NestedRefIdAdapterBase + { + UniversalId::Type mType; + + // not implemented + ActorAiRefIdAdapter (const ActorAiRefIdAdapter&); + ActorAiRefIdAdapter& operator= (const ActorAiRefIdAdapter&); + + public: + + ActorAiRefIdAdapter(UniversalId::Type type) :mType(type) {} + + virtual ~ActorAiRefIdAdapter() {} + + virtual void addNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int position) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + ESXRecordT actor = record.get(); + + std::vector& list = actor.mAiPackage.mList; + + ESM::AIPackage newRow; + newRow.mType = ESM::AI_CNDT; + newRow.mCellName = ""; + + if (position >= (int)list.size()) + list.push_back(newRow); + else + list.insert(list.begin()+position, newRow); + + record.setModified (actor); + } + + virtual void removeNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int rowToRemove) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + ESXRecordT actor = record.get(); + + std::vector& list = actor.mAiPackage.mList; + + if (rowToRemove < 0 || rowToRemove >= static_cast (list.size())) + throw std::runtime_error ("index out of range"); + + list.erase (list.begin () + rowToRemove); + + record.setModified (actor); + } + + virtual void setNestedTable (const RefIdColumn* column, + RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + ESXRecordT actor = record.get(); + + actor.mAiPackage.mList = + static_cast >&>(nestedTable).mNestedTable; + + record.setModified (actor); + } + + virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, + const RefIdData& data, int index) const + { + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + + // deleted by dtor of NestedTableStoring + return new NestedTableWrapper >(record.get().mAiPackage.mList); + } + + virtual QVariant getNestedData (const RefIdColumn *column, + const RefIdData& data, int index, int subRowIndex, int subColIndex) const + { + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + + const std::vector& list = record.get().mAiPackage.mList; + + if (subRowIndex < 0 || subRowIndex >= static_cast (list.size())) + throw std::runtime_error ("index out of range"); + + const ESM::AIPackage& content = list.at(subRowIndex); + + switch (subColIndex) + { + case 0: + switch (content.mType) + { + case ESM::AI_Wander: return QString("AI Wander"); + case ESM::AI_Travel: return QString("AI Travel"); + case ESM::AI_Follow: return QString("AI Follow"); + case ESM::AI_Escort: return QString("AI Escort"); + case ESM::AI_Activate: return QString("AI Activate"); + case ESM::AI_CNDT: + default: return QString("None"); + } + case 1: // wander dist + if (content.mType == ESM::AI_Wander) + return content.mWander.mDistance; + else + return QVariant(); + case 2: // wander dur + if (content.mType == ESM::AI_Wander || + content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) + return content.mWander.mDuration; + else + return QVariant(); + case 3: // wander ToD + if (content.mType == ESM::AI_Wander) + return content.mWander.mTimeOfDay; // FIXME: not sure of the format + else + return QVariant(); + case 4: // wander idle + if (content.mType == ESM::AI_Wander) + { + return static_cast(content.mWander.mIdle[0]); // FIXME: + } + else + return QVariant(); + case 5: // wander repeat + if (content.mType == ESM::AI_Wander) + return QString(content.mWander.mShouldRepeat ? "Yes" : "No"); + else + return QVariant(); + case 6: // activate name + if (content.mType == ESM::AI_Activate) + return QString(content.mActivate.mName.toString().c_str()); + else + return QVariant(); + case 7: // target id + if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) + return QString(content.mTarget.mId.toString().c_str()); + else + return QVariant(); + case 8: // target cell + if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) + return QString::fromUtf8(content.mCellName.c_str()); + else + return QVariant(); + case 9: + if (content.mType == ESM::AI_Travel) + return content.mTravel.mX; + else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) + return content.mTarget.mX; + else + return QVariant(); + case 10: + if (content.mType == ESM::AI_Travel) + return content.mTravel.mY; + else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) + return content.mTarget.mY; + else + return QVariant(); + case 11: + if (content.mType == ESM::AI_Travel) + return content.mTravel.mZ; + else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) + return content.mTarget.mZ; + else + return QVariant(); + default: + throw std::runtime_error("Trying to access non-existing column in the nested table!"); + } + } + + virtual void setNestedData (const RefIdColumn *column, + RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (row, mType))); + ESXRecordT actor = record.get(); + std::vector& list = actor.mAiPackage.mList; + + if (subRowIndex < 0 || subRowIndex >= static_cast (list.size())) + throw std::runtime_error ("index out of range"); + + ESM::AIPackage& content = list.at(subRowIndex); + + switch(subColIndex) + { + case 0: // ai package type + if ("AI Wander" == value.toString().toStdString()) + content.mType = ESM::AI_Wander; + else if ("AI Travel" == value.toString().toStdString()) + content.mType = ESM::AI_Travel; + else if ("AI Follow" == value.toString().toStdString()) + content.mType = ESM::AI_Follow; + else if ("AI Escort" == value.toString().toStdString()) + content.mType = ESM::AI_Escort; + else if ("AI Activate" == value.toString().toStdString()) + content.mType = ESM::AI_Activate; + else + content.mType = ESM::AI_CNDT; + break; // always save + + case 1: + if (content.mType == ESM::AI_Wander) + content.mWander.mDistance = static_cast(value.toInt()); + else + return; // return without saving + case 2: + if (content.mType == ESM::AI_Wander || + content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) + content.mWander.mDuration = static_cast(value.toInt()); + else + return; // return without saving + case 3: + if (content.mType == ESM::AI_Wander) + content.mWander.mTimeOfDay = static_cast(value.toInt()); + else + return; // return without saving + case 4: + if (content.mType == ESM::AI_Wander) + break; // FIXME: idle + else + return; // return without saving + case 5: + if (content.mType == ESM::AI_Wander) + { + if ("Yes" == value.toString().toStdString()) + content.mWander.mShouldRepeat = 1; + if ("No" == value.toString().toStdString()) + content.mWander.mShouldRepeat = 0; + else + return; // return without saving + } + case 6: // NAME32 + if (content.mType == ESM::AI_Activate) + { + content.mActivate.mName.assign(value.toString().toUtf8().constData()); + break; + } + else + return; // return without saving + case 7: // NAME32 + if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) + { + content.mTarget.mId.assign(value.toString().toUtf8().constData()); + break; + } + else + return; // return without saving + case 8: + if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) + { + content.mCellName = std::string(value.toString().toUtf8().constData()); + break; + } + else + return; // return without saving + case 9: + if (content.mType == ESM::AI_Travel) + content.mTravel.mZ = value.toFloat(); + else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) + content.mTarget.mZ = value.toFloat(); + else + return; // return without saving + case 10: + if (content.mType == ESM::AI_Travel) + content.mTravel.mZ = value.toFloat(); + else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) + content.mTarget.mZ = value.toFloat(); + else + return; // return without saving + case 11: + if (content.mType == ESM::AI_Travel) + content.mTravel.mZ = value.toFloat(); + else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) + content.mTarget.mZ = value.toFloat(); + else + return; // return without saving + default: + throw std::runtime_error("Trying to access non-existing column in the nested table!"); + } + + record.setModified (actor); + } + + virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const + { + return 12; + } + + virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const + { + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + + return static_cast(record.get().mAiPackage.mList.size()); + } + }; } #endif diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 3f14ba4ac..000426a0b 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -180,6 +180,42 @@ CSMWorld::RefIdCollection::RefIdCollection() mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_RotZ, CSMWorld::ColumnBase::Display_Float)); + // Nested table + mColumns.push_back(RefIdColumn (Columns::ColumnId_AiPackageList, ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); + actorsColumns.mAiPackages = &mColumns.back(); + + std::map aiMap; + aiMap.insert( + std::make_pair(UniversalId::Type_Npc, new ActorAiRefIdAdapter (UniversalId::Type_Npc))); + aiMap.insert( + std::make_pair(UniversalId::Type_Creature, new ActorAiRefIdAdapter (UniversalId::Type_Creature))); + + mNestedAdapters.push_back (std::make_pair(&mColumns.back(), aiMap)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_AiPackage, CSMWorld::ColumnBase::Display_String)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_AiWanderDist, CSMWorld::ColumnBase::Display_Integer)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_AiWanderDuration, CSMWorld::ColumnBase::Display_Integer)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_AiWanderToD, CSMWorld::ColumnBase::Display_Integer)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_AiWanderIdle, CSMWorld::ColumnBase::Display_Integer)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_AiWanderRepeat, CSMWorld::ColumnBase::Display_String)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_AiActivateName, CSMWorld::ColumnBase::Display_String)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_AiTargetId, CSMWorld::ColumnBase::Display_String)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_AiTargetCell, CSMWorld::ColumnBase::Display_String)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_PosX, CSMWorld::ColumnBase::Display_Float)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_PosY, CSMWorld::ColumnBase::Display_Float)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_PosZ, CSMWorld::ColumnBase::Display_Float)); + static const struct { int mName;