diff --git a/mapeditor/inspector/inspector.cpp b/mapeditor/inspector/inspector.cpp index e575f660a..097cbc3df 100644 --- a/mapeditor/inspector/inspector.cpp +++ b/mapeditor/inspector/inspector.cpp @@ -67,7 +67,8 @@ void Initializer::initialize(CGCreature * o) if(!o) return; o->character = CGCreature::Character::HOSTILE; - o->putStack(SlotID(0), new CStackInstance(CreatureID(o->subID), 0, false)); + if(!o->hasStackAtSlot(SlotID(0))) + o->putStack(SlotID(0), new CStackInstance(CreatureID(o->subID), 0, false)); } void Initializer::initialize(CGDwelling * o) diff --git a/mapeditor/mainwindow.cpp b/mapeditor/mainwindow.cpp index c119bee1f..b8408b0f5 100644 --- a/mapeditor/mainwindow.cpp +++ b/mapeditor/mainwindow.cpp @@ -1126,3 +1126,31 @@ void MainWindow::on_actionRecreate_obstacles_triggered() } + +void MainWindow::on_actionCut_triggered() +{ + if(controller.map()) + { + controller.copyToClipboard(mapLevel); + controller.commitObjectErase(mapLevel); + } +} + + +void MainWindow::on_actionCopy_triggered() +{ + if(controller.map()) + { + controller.copyToClipboard(mapLevel); + } +} + + +void MainWindow::on_actionPaste_triggered() +{ + if(controller.map()) + { + controller.pasteFromClipboard(mapLevel); + } +} + diff --git a/mapeditor/mainwindow.h b/mapeditor/mainwindow.h index 3464c45b2..ccd410aa8 100644 --- a/mapeditor/mainwindow.h +++ b/mapeditor/mainwindow.h @@ -102,6 +102,12 @@ private slots: void switchDefaultPlayer(const PlayerColor &); + void on_actionCut_triggered(); + + void on_actionCopy_triggered(); + + void on_actionPaste_triggered(); + public slots: void treeViewSelected(const QModelIndex &selected, const QModelIndex &deselected); diff --git a/mapeditor/mainwindow.ui b/mapeditor/mainwindow.ui index 6da92787e..766114052 100644 --- a/mapeditor/mainwindow.ui +++ b/mapeditor/mainwindow.ui @@ -79,6 +79,9 @@ + + + diff --git a/mapeditor/mapcontroller.cpp b/mapeditor/mapcontroller.cpp index aa3a0c92b..b94634334 100644 --- a/mapeditor/mapcontroller.cpp +++ b/mapeditor/mapcontroller.cpp @@ -20,6 +20,7 @@ #include "../lib/CSkillHandler.h" #include "../lib/spells/CSpellHandler.h" #include "../lib/CHeroHandler.h" +#include "../lib/serializer/CMemorySerializer.h" #include "mapview.h" #include "scenelayer.h" #include "maphandler.h" @@ -329,6 +330,48 @@ void MapController::commitObjectErase(int level) main->mapChanged(); } +void MapController::copyToClipboard(int level) +{ + _clipboard.clear(); + _clipboardShiftIndex = 0; + auto selectedObjects = _scenes[level]->selectionObjectsView.getSelection(); + for(auto * obj : selectedObjects) + { + assert(obj->pos.z == level); + _clipboard.push_back(CMemorySerializer::deepCopy(*obj)); + } +} + +void MapController::pasteFromClipboard(int level) +{ + _scenes[level]->selectionObjectsView.clear(); + + auto shift = int3::getDirs()[_clipboardShiftIndex++]; + if(_clipboardShiftIndex == int3::getDirs().size()) + _clipboardShiftIndex = 0; + + for(auto & objUniquePtr : _clipboard) + { + auto * obj = CMemorySerializer::deepCopy(*objUniquePtr).release(); + auto newPos = objUniquePtr->pos + shift; + if(_map->isInTheMap(newPos)) + obj->pos = newPos; + obj->pos.z = level; + + Initializer init(obj, defaultPlayer); + _map->getEditManager()->insertObject(obj); + _scenes[level]->selectionObjectsView.selectObject(obj); + _mapHandler->invalidate(obj); + } + + _scenes[level]->objectsView.draw(); + _scenes[level]->passabilityView.update(); + _scenes[level]->selectionObjectsView.draw(); + + _miniscenes[level]->updateViews(); + main->mapChanged(); +} + bool MapController::discardObject(int level) const { _scenes[level]->selectionObjectsView.clear(); diff --git a/mapeditor/mapcontroller.h b/mapeditor/mapcontroller.h index 3fef9adcc..1446225f6 100644 --- a/mapeditor/mapcontroller.h +++ b/mapeditor/mapcontroller.h @@ -49,6 +49,9 @@ public: void commitObjectCreate(int level); void commitObjectChange(int level); + void copyToClipboard(int level); + void pasteFromClipboard(int level); + bool discardObject(int level) const; void createObject(int level, CGObjectInstance * obj) const; bool canPlaceObject(int level, CGObjectInstance * obj, QString & error) const; @@ -64,6 +67,8 @@ private: MainWindow * main; mutable std::array, 2> _scenes; mutable std::array, 2> _miniscenes; + std::vector> _clipboard; + int _clipboardShiftIndex = 0; void connectScenes(); }; diff --git a/mapeditor/mapview.cpp b/mapeditor/mapview.cpp index 0cd4b9a5a..e24a990cb 100644 --- a/mapeditor/mapview.cpp +++ b/mapeditor/mapview.cpp @@ -269,14 +269,17 @@ void MapView::mousePressEvent(QMouseEvent *event) { if(event->button() == Qt::LeftButton) { - auto * obj = sc->selectionObjectsView.selectObjectAt(tileStart.x, tileStart.y); - if(obj) + //when paste, new object could be beyond initial object so we need to test two objects in order to select new one + //if object is pasted at place where is multiple objects then proper selection is not guaranteed + auto * firstSelectedObject = sc->selectionObjectsView.selectObjectAt(tileStart.x, tileStart.y); + auto * secondSelectedObject = sc->selectionObjectsView.selectObjectAt(tileStart.x, tileStart.y, firstSelectedObject); + if(firstSelectedObject) { - if(sc->selectionObjectsView.isSelected(obj)) + if(sc->selectionObjectsView.isSelected(firstSelectedObject)) { if(qApp->keyboardModifiers() & Qt::ControlModifier) { - sc->selectionObjectsView.deselectObject(obj); + sc->selectionObjectsView.deselectObject(firstSelectedObject); sc->selectionObjectsView.selectionMode = SelectionObjectsLayer::SELECTION; } else @@ -284,10 +287,13 @@ void MapView::mousePressEvent(QMouseEvent *event) } else { - if(!(qApp->keyboardModifiers() & Qt::ControlModifier)) - sc->selectionObjectsView.clear(); + if(!secondSelectedObject || !sc->selectionObjectsView.isSelected(secondSelectedObject)) + { + if(!(qApp->keyboardModifiers() & Qt::ControlModifier)) + sc->selectionObjectsView.clear(); + sc->selectionObjectsView.selectObject(firstSelectedObject); + } sc->selectionObjectsView.selectionMode = SelectionObjectsLayer::MOVEMENT; - sc->selectionObjectsView.selectObject(obj); } } else diff --git a/mapeditor/scenelayer.cpp b/mapeditor/scenelayer.cpp index c5b4a582f..cbfd842ce 100644 --- a/mapeditor/scenelayer.cpp +++ b/mapeditor/scenelayer.cpp @@ -410,7 +410,7 @@ void SelectionObjectsLayer::draw() redraw(); } -CGObjectInstance * SelectionObjectsLayer::selectObjectAt(int x, int y) const +CGObjectInstance * SelectionObjectsLayer::selectObjectAt(int x, int y, const CGObjectInstance * ignore) const { if(!map || !map->isInTheMap(int3(x, y, scene->level))) return nullptr; @@ -420,7 +420,7 @@ CGObjectInstance * SelectionObjectsLayer::selectObjectAt(int x, int y) const //visitable is most important for(auto & object : objects) { - if(!object.obj) + if(!object.obj || object.obj == ignore) continue; if(object.obj->visitableAt(x, y)) @@ -432,7 +432,7 @@ CGObjectInstance * SelectionObjectsLayer::selectObjectAt(int x, int y) const //if not visitable tile - try to get blocked for(auto & object : objects) { - if(!object.obj) + if(!object.obj || object.obj == ignore) continue; if(object.obj->blockingAt(x, y)) @@ -444,7 +444,7 @@ CGObjectInstance * SelectionObjectsLayer::selectObjectAt(int x, int y) const //finally, we can take any object for(auto & object : objects) { - if(!object.obj) + if(!object.obj || object.obj == ignore) continue; if(object.obj->coveringAt(x, y)) diff --git a/mapeditor/scenelayer.h b/mapeditor/scenelayer.h index f796701bc..3d3cd6a3f 100644 --- a/mapeditor/scenelayer.h +++ b/mapeditor/scenelayer.h @@ -142,7 +142,7 @@ public: void draw(); - CGObjectInstance * selectObjectAt(int x, int y) const; + CGObjectInstance * selectObjectAt(int x, int y, const CGObjectInstance * ignore = nullptr) const; void selectObjects(int x1, int y1, int x2, int y2); void selectObject(CGObjectInstance *, bool inform = true); void deselectObject(CGObjectInstance *);