1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-22 22:13:35 +02:00

- Erase operation (hotkey: DELETE)

- Interface is updated when there is an object selected or not
- Undo for erase object
This commit is contained in:
Tomasz Zieliński 2022-09-06 08:05:34 +02:00
parent d0a6bf9521
commit d3da75eece
13 changed files with 163 additions and 18 deletions

View File

@ -233,7 +233,8 @@ CMapHeader::~CMapHeader()
CMap::CMap()
: checksum(0), grailPos(-1, -1, -1), grailRadius(0), terrain(nullptr),
guardingCreaturePositions(nullptr)
guardingCreaturePositions(nullptr),
uid_counter(0)
{
allHeroes.resize(allowedHeroes.size());
allowedAbilities = VLC->skillh->getDefaultAllowed();
@ -603,22 +604,36 @@ void CMap::addNewQuestInstance(CQuest* quest)
quests.push_back(quest);
}
void CMap::setUniqueInstanceName(CGObjectInstance* obj)
{
//this gives object unique name even if objects are removed later
auto uid = uid_counter++;
boost::format fmt("%s_%d");
fmt % obj->typeName % uid;
obj->instanceName = fmt.str();
}
void CMap::addNewObject(CGObjectInstance * obj)
{
if(obj->id != ObjectInstanceID((si32)objects.size()))
throw std::runtime_error("Invalid object instance id");
if(obj->instanceName == "")
throw std::runtime_error("Object instance name missing");
auto it = instanceNames.find(obj->instanceName);
if(it != instanceNames.end())
if (vstd::contains(instanceNames, obj->instanceName))
throw std::runtime_error("Object instance name duplicated: "+obj->instanceName);
objects.push_back(obj);
instanceNames[obj->instanceName] = obj;
addBlockVisTiles(obj);
//TODO: how about deafeated heroes recruited again?
//TODO: How about objects restored with map editor?
obj->afterAddToMap(this);
}

View File

@ -362,6 +362,7 @@ public:
void addNewQuestInstance(CQuest * quest);
void setUniqueInstanceName(CGObjectInstance* obj);
///Use only this method when creating new map object instances
void addNewObject(CGObjectInstance * obj);
void moveObject(CGObjectInstance * obj, const int3 & dst);
@ -411,6 +412,7 @@ public:
private:
/// a 3-dimensional array of terrain tiles, access is as follows: x, y, level. where level=1 is underground
TerrainTile*** terrain;
si32 uid_counter; //TODO: initialize when loading an old map
public:
template <typename Handler>
@ -488,5 +490,6 @@ public:
h & CGTownInstance::universitySkills;
h & instanceNames;
h & uid_counter;
}
};

View File

@ -190,6 +190,7 @@ void CMapUndoManager::setUndoRedoLimit(int value)
assert(value >= 0);
undoStack.resize(std::min(undoStack.size(), static_cast<TStack::size_type>(value)));
redoStack.resize(std::min(redoStack.size(), static_cast<TStack::size_type>(value)));
onUndoRedo();
}
const CMapOperation * CMapUndoManager::peekRedo() const
@ -207,6 +208,7 @@ void CMapUndoManager::addOperation(std::unique_ptr<CMapOperation> && operation)
undoStack.push_front(std::move(operation));
if(undoStack.size() > undoRedoLimit) undoStack.pop_back();
redoStack.clear();
onUndoRedo();
}
void CMapUndoManager::doOperation(TStack & fromStack, TStack & toStack, bool doUndo)
@ -1077,11 +1079,9 @@ CInsertObjectOperation::CInsertObjectOperation(CMap * map, CGObjectInstance * ob
void CInsertObjectOperation::execute()
{
obj->id = ObjectInstanceID((si32)map->objects.size());
obj->id = ObjectInstanceID(map->objects.size());
boost::format fmt("%s_%d");
fmt % obj->typeName % obj->id.getNum();
obj->instanceName = fmt.str();
map->setUniqueInstanceName(obj);
map->addNewObject(obj);
}
@ -1112,13 +1112,11 @@ CMoveObjectOperation::CMoveObjectOperation(CMap * map, CGObjectInstance * obj, c
void CMoveObjectOperation::execute()
{
map->moveObject(obj, targetPos);
logGlobal->debug("Moved object %s to position %s", obj->instanceName, targetPos.toString());
}
void CMoveObjectOperation::undo()
{
map->moveObject(obj, initialPos);
logGlobal->debug("Moved object %s back to position %s", obj->instanceName, initialPos.toString());
}
void CMoveObjectOperation::redo()
@ -1137,6 +1135,23 @@ CRemoveObjectOperation::CRemoveObjectOperation(CMap * map, CGObjectInstance * ob
}
CRemoveObjectOperation::~CRemoveObjectOperation()
{
//when operation is destroyed and wasn't undone, the object is lost forever
if (!obj)
{
return;
}
//do not destroy an object that belongs to map
if (!vstd::contains(map->instanceNames, obj->instanceName))
{
delete obj;
obj = nullptr;
}
}
void CRemoveObjectOperation::execute()
{
map->removeObject(obj);
@ -1144,7 +1159,16 @@ void CRemoveObjectOperation::execute()
void CRemoveObjectOperation::undo()
{
//TODO
try
{
//set new id, but do not rename object
obj->id = ObjectInstanceID((si32)map->objects.size());
map->addNewObject(obj);
}
catch (const std::exception& e)
{
logGlobal->error(e.what());
}
}
void CRemoveObjectOperation::redo()

View File

@ -451,6 +451,7 @@ class CRemoveObjectOperation : public CMapOperation
{
public:
CRemoveObjectOperation(CMap * map, CGObjectInstance * obj);
~CRemoveObjectOperation();
void execute() override;
void undo() override;

View File

@ -657,6 +657,10 @@ void MainWindow::on_toolArea_clicked(bool checked)
ui->mapView->selectionTool = MapView::SelectionTool::None;
}
void MainWindow::on_actionErase_triggered()
{
on_toolErase_clicked();
}
void MainWindow::on_toolErase_clicked()
{
@ -799,4 +803,16 @@ void MainWindow::enableUndo(bool enable)
void MainWindow::enableRedo(bool enable)
{
ui->actionRedo->setEnabled(enable);
}
void MainWindow::onSelectionMade(int level, bool anythingSelected)
{
if (level == mapLevel)
{
auto info = QString::asprintf("Selection on layer %s: %s", level, anythingSelected ? "true" : "false");
setStatusMessage(info);
ui->actionErase->setEnabled(anythingSelected);
ui->toolErase->setEnabled(anythingSelected);
}
}

View File

@ -48,6 +48,8 @@ private slots:
void on_actionLevel_triggered();
void on_actionSave_triggered();
void on_actionErase_triggered();
void on_actionUndo_triggered();
@ -90,6 +92,7 @@ public slots:
void mapChanged();
void enableUndo(bool enable);
void enableRedo(bool enable);
void onSelectionMade(int level, bool anythingSelected);
private:
void preparePreview(const QModelIndex &index, bool createNew);

View File

@ -76,6 +76,7 @@
</property>
<addaction name="actionUndo"/>
<addaction name="actionRedo"/>
<addaction name="actionErase"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuEdit"/>
@ -99,6 +100,7 @@
<addaction name="actionGrid"/>
<addaction name="actionPass"/>
<addaction name="separator"/>
<addaction name="actionErase"/>
<addaction name="actionCut"/>
<addaction name="actionCopy"/>
<addaction name="actionPaste"/>
@ -600,6 +602,9 @@
</item>
<item row="2" column="1">
<widget class="QPushButton" name="toolErase">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -655,7 +660,7 @@
<x>0</x>
<y>0</y>
<width>128</width>
<height>356</height>
<height>344</height>
</rect>
</property>
<property name="sizePolicy">
@ -698,7 +703,7 @@
<x>0</x>
<y>0</y>
<width>128</width>
<height>356</height>
<height>344</height>
</rect>
</property>
<property name="sizePolicy">
@ -717,7 +722,7 @@
<x>0</x>
<y>0</y>
<width>128</width>
<height>356</height>
<height>344</height>
</rect>
</property>
<property name="sizePolicy">
@ -862,6 +867,17 @@
<bool>true</bool>
</property>
</action>
<action name="actionErase">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Erase</string>
</property>
<property name="shortcut">
<string>Del</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>

View File

@ -18,6 +18,19 @@ MapController::MapController(MainWindow * m): main(m)
{
_scenes[0].reset(new MapScene(0));
_scenes[1].reset(new MapScene(1));
connectScenes();
}
void MapController::connectScenes()
{
for (int level = 0; level <= 1; level++)
{
//selections for both layers will be handled separately
QObject::connect(_scenes[level].get(), &MapScene::selected, [this, level](bool anythingSelected)
{
main->onSelectionMade(level, anythingSelected);
});
}
}
MapController::~MapController()
@ -47,6 +60,8 @@ void MapController::setMap(std::unique_ptr<CMap> cmap)
resetMapHandler();
sceneForceUpdate();
connectScenes();
_map->getEditManager()->getUndoManager().setUndoCallback([this](bool allowUndo, bool allowRedo)
{
main->enableUndo(allowUndo);
@ -99,7 +114,7 @@ void MapController::commitObjectErase(int level)
for(auto * obj : _scenes[level]->selectionObjectsView.getSelection())
{
_map->getEditManager()->removeObject(obj);
delete obj;
//object ownership goes to EditManager, which can handle undo operation
}
_scenes[level]->selectionObjectsView.clear();
resetMapHandler();

View File

@ -46,6 +46,8 @@ private:
std::unique_ptr<MapHandler> _mapHandler;
MainWindow * main;
mutable std::array<std::unique_ptr<MapScene>, 2> _scenes;
void connectScenes();
};
#endif // MAPCONTROLLER_H

View File

@ -281,7 +281,9 @@ MapScene::MapScene(int lev):
terrainView(this),
objectsView(this),
selectionObjectsView(this),
level(lev)
level(lev),
isTerrainSelected(false),
isObjectSelected(false)
{
}
@ -314,3 +316,15 @@ void MapScene::updateViews()
selectionTerrainView.show(true);
selectionObjectsView.show(true);
}
void MapScene::terrainSelected(bool anythingSelected)
{
isTerrainSelected = anythingSelected;
emit selected(isTerrainSelected || isObjectSelected);
}
void MapScene::objectSelected(bool anythingSelected)
{
isObjectSelected = anythingSelected;
emit selected(isTerrainSelected || isObjectSelected);
}

View File

@ -13,6 +13,7 @@ class MapController;
class MapScene : public QGraphicsScene
{
Q_OBJECT;
public:
MapScene(int lev);
@ -27,9 +28,19 @@ public:
SelectionObjectsLayer selectionObjectsView;
const int level;
signals:
void selected(bool anything);
public slots:
void terrainSelected(bool anything);
void objectSelected(bool anything);
private:
std::list<AbstractLayer *> getAbstractLayers();
bool isTerrainSelected;
bool isObjectSelected;
};
class MapView : public QGraphicsView

View File

@ -133,6 +133,7 @@ void SelectionTerrainLayer::update()
area.clear();
areaAdd.clear();
areaErase.clear();
selectionMade();
pixmap.reset(new QPixmap(map->width * 32, map->height * 32));
pixmap->fill(QColor(0, 0, 0, 0));
@ -173,6 +174,7 @@ void SelectionTerrainLayer::select(const int3 & tile)
areaAdd.insert(tile);
areaErase.erase(tile);
}
selectionMade();
}
void SelectionTerrainLayer::erase(const int3 & tile)
@ -186,6 +188,7 @@ void SelectionTerrainLayer::erase(const int3 & tile)
areaErase.insert(tile);
areaAdd.erase(tile);
}
selectionMade();
}
void SelectionTerrainLayer::clear()
@ -193,6 +196,7 @@ void SelectionTerrainLayer::clear()
areaErase = area;
areaAdd.clear();
area.clear();
selectionMade();
}
const std::set<int3> & SelectionTerrainLayer::selection() const
@ -200,6 +204,11 @@ const std::set<int3> & SelectionTerrainLayer::selection() const
return area;
}
void SelectionTerrainLayer::selectionMade()
{
scene->objectSelected(!area.empty());
}
TerrainLayer::TerrainLayer(MapScene * s): AbstractLayer(s)
{
}
@ -352,6 +361,7 @@ void SelectionObjectsLayer::update()
return;
selectedObjects.clear();
selectionMade();
shift = QPoint();
if(newObject)
delete newObject;
@ -465,14 +475,19 @@ void SelectionObjectsLayer::selectObjects(int x1, int y1, int x2, int y2)
for(int i = x1; i < x2; ++i)
{
for(auto & o : handler->getObjects(i, j, scene->level))
selectObject(o.obj);
selectObject(o.obj, false); //do not inform about each object added
}
}
selectionMade();
}
void SelectionObjectsLayer::selectObject(CGObjectInstance * obj)
void SelectionObjectsLayer::selectObject(CGObjectInstance * obj, bool inform /* = true */)
{
selectedObjects.insert(obj);
if (inform)
{
selectionMade();
}
}
bool SelectionObjectsLayer::isSelected(const CGObjectInstance * obj) const
@ -488,6 +503,12 @@ std::set<CGObjectInstance*> SelectionObjectsLayer::getSelection() const
void SelectionObjectsLayer::clear()
{
selectedObjects.clear();
selectionMade();
shift.setX(0);
shift.setY(0);
}
void SelectionObjectsLayer::selectionMade()
{
scene->objectSelected(!selectedObjects.empty());
}

View File

@ -67,6 +67,8 @@ public:
private:
std::set<int3> area, areaAdd, areaErase;
void selectionMade(); //TODO: consider making it a signal
};
@ -113,7 +115,7 @@ public:
CGObjectInstance * selectObjectAt(int x, int y) const;
void selectObjects(int x1, int y1, int x2, int y2);
void selectObject(CGObjectInstance *);
void selectObject(CGObjectInstance *, bool inform = true);
bool isSelected(const CGObjectInstance *) const;
std::set<CGObjectInstance*> getSelection() const;
void moveSelection(int x, int y);
@ -125,6 +127,8 @@ public:
private:
std::set<CGObjectInstance *> selectedObjects;
void selectionMade(); //TODO: consider making it a signal
};
#endif // SCENELAYER_H