diff --git a/apps/opencs/model/prefs/state.cpp b/apps/opencs/model/prefs/state.cpp index 2d511c3b50..61d64b783e 100644 --- a/apps/opencs/model/prefs/state.cpp +++ b/apps/opencs/model/prefs/state.cpp @@ -410,6 +410,7 @@ void CSMPrefs::State::declare() declareShortcut("scene-edit-abort", "Abort", QKeySequence(Qt::Key_Escape)); declareShortcut("scene-focus-toolbar", "Toggle Toolbar Focus", QKeySequence(Qt::Key_T)); declareShortcut("scene-render-stats", "Debug Rendering Stats", QKeySequence(Qt::Key_F3)); + declareShortcut("scene-duplicate", "Duplicate Instance", QKeySequence(Qt::ShiftModifier | Qt::Key_C)); declareSubcategory("1st/Free Camera"); declareShortcut("free-forward", "Forward", QKeySequence(Qt::Key_W)); diff --git a/apps/opencs/model/prefs/values.hpp b/apps/opencs/model/prefs/values.hpp index 247c025e80..4683258e57 100644 --- a/apps/opencs/model/prefs/values.hpp +++ b/apps/opencs/model/prefs/values.hpp @@ -464,6 +464,7 @@ namespace CSMPrefs "scene-instance-drop-terrain-separately", "" }; Settings::SettingValue mSceneInstanceDropCollisionSeparately{ mIndex, sName, "scene-instance-drop-collision-separately", "" }; + Settings::SettingValue mSceneDuplicate{ mIndex, sName, "scene-duplicate", "Shift+C" }; Settings::SettingValue mSceneLoadCamCell{ mIndex, sName, "scene-load-cam-cell", "Keypad+5" }; Settings::SettingValue mSceneLoadCamEastcell{ mIndex, sName, "scene-load-cam-eastcell", "Keypad+6" }; diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 1afa9027a9..642edcfb64 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -297,6 +297,7 @@ void CSMWorld::RefCollection::cloneRecord( const ESM::RefId& origin, const ESM::RefId& destination, const UniversalId::Type type) { auto copy = std::make_unique>(); + int index = getAppendIndex(ESM::RefId(), type); copy->mModified = getRecord(origin).get(); copy->mState = RecordBase::State_ModifiedOnly; @@ -304,6 +305,15 @@ void CSMWorld::RefCollection::cloneRecord( copy->get().mId = destination; copy->get().mIdNum = extractIdNum(destination.getRefIdString()); + if (copy->get().mRefNum.mContentFile != 0) + { + mRefIndex.insert(std::make_pair(static_cast*>(copy.get())->get().mIdNum, index)); + copy->get().mRefNum.mContentFile = 0; + copy->get().mRefNum.mIndex = index; + } + else + copy->get().mRefNum.mIndex = copy->get().mIdNum; + insertRecord(std::move(copy), getAppendIndex(destination, type)); // call RefCollection::insertRecord() } diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index df5bb02332..7332d9c84b 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -202,15 +202,21 @@ CSVRender::InstanceMode::InstanceMode( connect(this, &InstanceMode::requestFocus, worldspaceWidget, &WorldspaceWidget::requestFocus); CSMPrefs::Shortcut* deleteShortcut = new CSMPrefs::Shortcut("scene-delete", worldspaceWidget); + connect(deleteShortcut, qOverload<>(&CSMPrefs::Shortcut::activated), this, &InstanceMode::deleteSelectedInstances); + + CSMPrefs::Shortcut* duplicateShortcut = new CSMPrefs::Shortcut("scene-duplicate", worldspaceWidget); + connect( - deleteShortcut, qOverload(&CSMPrefs::Shortcut::activated), this, &InstanceMode::deleteSelectedInstances); + duplicateShortcut, qOverload<>(&CSMPrefs::Shortcut::activated), this, &InstanceMode::cloneSelectedInstances); // Following classes could be simplified by using QSignalMapper, which is obsolete in Qt5.10, but not in Qt4.8 and // Qt5.14 CSMPrefs::Shortcut* dropToCollisionShortcut = new CSMPrefs::Shortcut("scene-instance-drop-collision", worldspaceWidget); + connect(dropToCollisionShortcut, qOverload<>(&CSMPrefs::Shortcut::activated), this, &InstanceMode::dropSelectedInstancesToCollision); + CSMPrefs::Shortcut* dropToTerrainLevelShortcut = new CSMPrefs::Shortcut("scene-instance-drop-terrain", worldspaceWidget); connect(dropToTerrainLevelShortcut, qOverload<>(&CSMPrefs::Shortcut::activated), this, @@ -1068,7 +1074,7 @@ void CSVRender::InstanceMode::handleSelectDrag(const QPoint& pos) mDragMode = DragMode_None; } -void CSVRender::InstanceMode::deleteSelectedInstances(bool active) +void CSVRender::InstanceMode::deleteSelectedInstances() { std::vector> selection = getWorldspaceWidget().getSelection(Mask_Reference); if (selection.empty()) @@ -1087,6 +1093,27 @@ void CSVRender::InstanceMode::deleteSelectedInstances(bool active) getWorldspaceWidget().clearSelection(Mask_Reference); } +void CSVRender::InstanceMode::cloneSelectedInstances() +{ + std::vector> selection = getWorldspaceWidget().getSelection(Mask_Reference); + if (selection.empty()) + return; + + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + CSMWorld::IdTable& referencesTable + = dynamic_cast(*document.getData().getTableModel(CSMWorld::UniversalId::Type_References)); + QUndoStack& undoStack = document.getUndoStack(); + + CSMWorld::CommandMacro macro(undoStack, "Clone Instances"); + for (osg::ref_ptr tag : selection) + if (CSVRender::ObjectTag* objectTag = dynamic_cast(tag.get())) + { + macro.push(new CSMWorld::CloneCommand(referencesTable, objectTag->mObject->getReferenceId(), + "ref#" + std::to_string(referencesTable.rowCount()), CSMWorld::UniversalId::Type_Reference)); + } + // getWorldspaceWidget().clearSelection(Mask_Reference); +} + void CSVRender::InstanceMode::dropInstance(CSVRender::Object* object, float dropHeight) { object->setEdited(Object::Override_Position); diff --git a/apps/opencs/view/render/instancemode.hpp b/apps/opencs/view/render/instancemode.hpp index 5055d08d5b..917fde301a 100644 --- a/apps/opencs/view/render/instancemode.hpp +++ b/apps/opencs/view/render/instancemode.hpp @@ -131,7 +131,8 @@ namespace CSVRender private slots: void subModeChanged(const std::string& id); - void deleteSelectedInstances(bool active); + void deleteSelectedInstances(); + void cloneSelectedInstances(); void dropSelectedInstancesToCollision(); void dropSelectedInstancesToTerrain(); void dropSelectedInstancesToCollisionSeparately();