2012-06-29 16:44:26 +02:00
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
// This file is part of Remere's Map Editor
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
2020-07-30 11:42:28 -03:00
|
|
|
// Remere's Map Editor is free software: you can redistribute it and/or modify
|
2012-06-29 16:44:26 +02:00
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
// (at your option) any later version.
|
|
|
|
|
//
|
2020-07-30 11:42:28 -03:00
|
|
|
// Remere's Map Editor is distributed in the hope that it will be useful,
|
2012-06-29 16:44:26 +02:00
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2020-07-30 11:42:28 -03:00
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2012-06-29 16:44:26 +02:00
|
|
|
// GNU General Public License for more details.
|
|
|
|
|
//
|
|
|
|
|
// You should have received a copy of the GNU General Public License
|
2020-07-30 11:42:28 -03:00
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2012-06-29 16:44:26 +02:00
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
#include "main.h"
|
|
|
|
|
|
|
|
|
|
#include "action.h"
|
|
|
|
|
#include "settings.h"
|
|
|
|
|
#include "map.h"
|
|
|
|
|
#include "editor.h"
|
|
|
|
|
#include "gui.h"
|
|
|
|
|
|
2014-01-22 20:16:17 +01:00
|
|
|
Change::Change() : type(CHANGE_NONE), data(nullptr)
|
2012-06-29 16:44:26 +02:00
|
|
|
{
|
2015-12-06 11:42:37 -03:00
|
|
|
////
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
Change::Change(Tile* tile) : type(CHANGE_TILE)
|
2012-06-29 16:44:26 +02:00
|
|
|
{
|
2023-01-27 17:41:20 -03:00
|
|
|
ASSERT(tile);
|
|
|
|
|
data = tile;
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
Change* Change::Create(House* house, const Position& position)
|
2012-06-29 16:44:26 +02:00
|
|
|
{
|
2023-01-27 17:41:20 -03:00
|
|
|
Change* change = new Change();
|
|
|
|
|
change->type = CHANGE_MOVE_HOUSE_EXIT;
|
|
|
|
|
change->data = new HouseData { house->id, position };
|
|
|
|
|
return change;
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
Change* Change::Create(Waypoint* waypoint, const Position& position)
|
2012-06-29 16:44:26 +02:00
|
|
|
{
|
2023-01-27 17:41:20 -03:00
|
|
|
Change* change = new Change();
|
|
|
|
|
change->type = CHANGE_MOVE_WAYPOINT;
|
|
|
|
|
change->data = new WaypointData { waypoint->name, position };
|
|
|
|
|
return change;
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Change::~Change()
|
|
|
|
|
{
|
|
|
|
|
clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Change::clear()
|
|
|
|
|
{
|
2015-12-06 11:42:37 -03:00
|
|
|
switch(type) {
|
2012-06-29 16:44:26 +02:00
|
|
|
case CHANGE_TILE:
|
|
|
|
|
ASSERT(data);
|
|
|
|
|
delete reinterpret_cast<Tile*>(data);
|
|
|
|
|
break;
|
|
|
|
|
case CHANGE_MOVE_HOUSE_EXIT:
|
|
|
|
|
ASSERT(data);
|
2023-01-27 17:41:20 -03:00
|
|
|
delete reinterpret_cast<HouseData*>(data);
|
2012-06-29 16:44:26 +02:00
|
|
|
break;
|
|
|
|
|
case CHANGE_MOVE_WAYPOINT:
|
|
|
|
|
ASSERT(data);
|
2023-01-27 17:41:20 -03:00
|
|
|
delete reinterpret_cast<WaypointData*>(data);
|
2012-06-29 16:44:26 +02:00
|
|
|
break;
|
|
|
|
|
case CHANGE_NONE:
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
#ifdef __DEBUG_MODE__
|
|
|
|
|
if(data)
|
|
|
|
|
printf("UNHANDLED CHANGE TYPE! Leak!");
|
|
|
|
|
#endif
|
|
|
|
|
break;
|
|
|
|
|
}
|
2023-01-27 17:41:20 -03:00
|
|
|
|
2012-06-29 16:44:26 +02:00
|
|
|
type = CHANGE_NONE;
|
2014-01-22 20:16:17 +01:00
|
|
|
data = nullptr;
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
2014-02-28 22:34:25 +01:00
|
|
|
uint32_t Change::memsize() const
|
2012-06-29 16:44:26 +02:00
|
|
|
{
|
2014-02-28 22:34:25 +01:00
|
|
|
uint32_t mem = sizeof(*this);
|
2023-01-27 17:41:20 -03:00
|
|
|
if(type == CHANGE_TILE) {
|
|
|
|
|
mem += reinterpret_cast<Tile*>(data)->memsize();
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
return mem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Action::Action(Editor& editor, ActionIdentifier ident) :
|
|
|
|
|
commited(false),
|
|
|
|
|
editor(editor),
|
|
|
|
|
type(ident)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Action::~Action()
|
|
|
|
|
{
|
2023-01-27 17:41:20 -03:00
|
|
|
for(Change* change : changes) {
|
|
|
|
|
delete change;
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
2023-01-27 17:41:20 -03:00
|
|
|
changes.clear();
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t Action::approx_memsize() const
|
|
|
|
|
{
|
2014-02-28 22:34:25 +01:00
|
|
|
uint32_t mem = sizeof(*this);
|
2012-06-29 16:44:26 +02:00
|
|
|
mem += changes.size() * (sizeof(Change) + sizeof(Tile) + sizeof(Item) + 6/* approx overhead*/);
|
|
|
|
|
return mem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t Action::memsize() const
|
|
|
|
|
{
|
2014-02-28 22:34:25 +01:00
|
|
|
uint32_t mem = sizeof(*this);
|
2012-06-29 16:44:26 +02:00
|
|
|
mem += sizeof(Change*) * 3 * changes.size();
|
2015-12-06 11:42:37 -03:00
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
for(const Change* change : changes) {
|
|
|
|
|
if(change && change->getType() == CHANGE_TILE) {
|
|
|
|
|
mem += reinterpret_cast<Tile*>(change->getData())->memsize();
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-01-27 17:41:20 -03:00
|
|
|
|
2012-06-29 16:44:26 +02:00
|
|
|
return mem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Action::commit(DirtyList* dirty_list)
|
|
|
|
|
{
|
2023-01-27 17:41:20 -03:00
|
|
|
Map& map = editor.getMap();
|
2023-01-26 11:17:55 -03:00
|
|
|
Selection& selection = editor.getSelection();
|
|
|
|
|
selection.start(Selection::INTERNAL);
|
2023-01-27 17:41:20 -03:00
|
|
|
|
|
|
|
|
for (Change* change : changes) {
|
|
|
|
|
switch(change->getType()) {
|
2015-12-06 11:42:37 -03:00
|
|
|
case CHANGE_TILE: {
|
2023-01-27 17:41:20 -03:00
|
|
|
void** data = &change->data;
|
|
|
|
|
Tile* new_tile = reinterpret_cast<Tile*>(*data);
|
|
|
|
|
ASSERT(new_tile);
|
|
|
|
|
|
|
|
|
|
const Position& pos = new_tile->getPosition();
|
2016-09-29 19:24:45 -03:00
|
|
|
|
2015-12-06 11:42:37 -03:00
|
|
|
if(editor.IsLiveClient()) {
|
2023-01-27 17:41:20 -03:00
|
|
|
QTreeNode* node = map.getLeaf(pos.x, pos.y);
|
2023-01-29 23:32:19 -03:00
|
|
|
if(!node || !node->isVisible(pos.z > rme::MapGroundLayer)) {
|
2023-01-27 17:41:20 -03:00
|
|
|
change->clear();
|
2012-06-29 16:44:26 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
Tile* old_tile = map.swapTile(pos, new_tile);
|
|
|
|
|
TileLocation* location = new_tile->getLocation();
|
2016-09-29 19:24:45 -03:00
|
|
|
|
2012-06-29 16:44:26 +02:00
|
|
|
// Update other nodes in the network
|
|
|
|
|
if(editor.IsLiveServer() && dirty_list)
|
|
|
|
|
dirty_list->AddPosition(pos.x, pos.y, pos.z);
|
|
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
new_tile->update();
|
2012-06-29 16:44:26 +02:00
|
|
|
|
|
|
|
|
//std::cout << "\tSwitched tile at " << pos.x << ";" << pos.y << ";" << pos.z << " from " << (void*)oldtile << " to " << *data << std::endl;
|
2023-01-27 17:41:20 -03:00
|
|
|
if(new_tile->isSelected())
|
|
|
|
|
selection.addInternal(new_tile);
|
2012-06-29 16:44:26 +02:00
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
if(old_tile) {
|
|
|
|
|
if(new_tile->getHouseID() != old_tile->getHouseID()) {
|
2012-06-29 16:44:26 +02:00
|
|
|
// oooooomggzzz we need to add it to the appropriate house!
|
2023-01-27 17:41:20 -03:00
|
|
|
House* house = map.houses.getHouse(old_tile->getHouseID());
|
2012-06-29 16:44:26 +02:00
|
|
|
if(house)
|
2023-01-27 17:41:20 -03:00
|
|
|
house->removeTile(old_tile);
|
2012-06-29 16:44:26 +02:00
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
house = map.houses.getHouse(new_tile->getHouseID());
|
2012-06-29 16:44:26 +02:00
|
|
|
if(house)
|
2023-01-27 17:41:20 -03:00
|
|
|
house->addTile(new_tile);
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
2023-01-27 17:41:20 -03:00
|
|
|
if(old_tile->spawn) {
|
|
|
|
|
if(new_tile->spawn) {
|
|
|
|
|
if(*old_tile->spawn != *new_tile->spawn) {
|
|
|
|
|
map.removeSpawn(old_tile);
|
|
|
|
|
map.addSpawn(new_tile);
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
2015-12-06 11:42:37 -03:00
|
|
|
} else {
|
2023-01-27 17:41:20 -03:00
|
|
|
map.removeSpawn(old_tile);
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
2023-01-27 17:41:20 -03:00
|
|
|
} else if(new_tile->spawn) {
|
|
|
|
|
map.addSpawn(new_tile);
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//oldtile->update();
|
2023-01-27 17:41:20 -03:00
|
|
|
if(old_tile->isSelected())
|
|
|
|
|
selection.removeInternal(old_tile);
|
2012-06-29 16:44:26 +02:00
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
*data = old_tile;
|
2015-12-06 11:42:37 -03:00
|
|
|
} else {
|
2023-01-27 17:41:20 -03:00
|
|
|
*data = map.allocator(location);
|
|
|
|
|
if(new_tile->getHouseID() != 0) {
|
2012-06-29 16:44:26 +02:00
|
|
|
// oooooomggzzz we need to add it to the appropriate house!
|
2023-01-27 17:41:20 -03:00
|
|
|
House* house = map.houses.getHouse(new_tile->getHouseID());
|
2015-12-06 11:42:37 -03:00
|
|
|
if(house) {
|
2023-01-27 17:41:20 -03:00
|
|
|
house->addTile(new_tile);
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
if(new_tile->spawn)
|
|
|
|
|
map.addSpawn(new_tile);
|
2012-06-29 16:44:26 +02:00
|
|
|
|
|
|
|
|
}
|
2023-01-27 17:41:20 -03:00
|
|
|
new_tile->modify();
|
2012-06-29 16:44:26 +02:00
|
|
|
|
|
|
|
|
// Update client dirty list
|
2015-12-06 11:42:37 -03:00
|
|
|
if(editor.IsLiveClient() && dirty_list && type != ACTION_REMOTE) {
|
2023-01-27 17:41:20 -03:00
|
|
|
dirty_list->AddChange(change);
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
2015-12-06 11:42:37 -03:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case CHANGE_MOVE_HOUSE_EXIT: {
|
2023-01-27 17:41:20 -03:00
|
|
|
HouseData* data = reinterpret_cast<HouseData*>(change->data);
|
|
|
|
|
ASSERT(data);
|
|
|
|
|
|
|
|
|
|
House* house = map.houses.getHouse(data->id);
|
|
|
|
|
if(house) {
|
|
|
|
|
const Position& old_pos = house->getExit();
|
|
|
|
|
house->setExit(data->position);
|
|
|
|
|
data->position = old_pos;
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
2015-12-06 11:42:37 -03:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case CHANGE_MOVE_WAYPOINT: {
|
2023-01-27 17:41:20 -03:00
|
|
|
WaypointData* data = reinterpret_cast<WaypointData*>(change->data);
|
|
|
|
|
ASSERT(data);
|
2012-06-29 16:44:26 +02:00
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
Waypoint* waypoint = map.waypoints.getWaypoint(data->id);
|
|
|
|
|
if(waypoint) {
|
|
|
|
|
TileLocation* old_tile = map.getTileL(waypoint->pos);
|
|
|
|
|
TileLocation* new_tile = map.getTileL(data->position);
|
2016-09-29 19:24:45 -03:00
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
if(data->position.isValid() && old_tile && old_tile->getWaypointCount() > 0)
|
|
|
|
|
old_tile->decreaseWaypointCount();
|
2012-06-29 16:44:26 +02:00
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
new_tile->increaseWaypointCount();
|
2012-06-29 16:44:26 +02:00
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
Position old_pos = waypoint->pos;
|
|
|
|
|
waypoint->pos = data->position;
|
|
|
|
|
data->position = old_pos;
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
2015-12-06 11:42:37 -03:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-29 16:44:26 +02:00
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-26 11:17:55 -03:00
|
|
|
selection.finish(Selection::INTERNAL);
|
2012-06-29 16:44:26 +02:00
|
|
|
commited = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Action::undo(DirtyList* dirty_list)
|
|
|
|
|
{
|
|
|
|
|
if(changes.empty())
|
|
|
|
|
return;
|
|
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
Map& map = editor.getMap();
|
2023-01-26 11:17:55 -03:00
|
|
|
Selection& selection = editor.getSelection();
|
|
|
|
|
selection.start(Selection::INTERNAL);
|
2012-06-29 16:44:26 +02:00
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
for (Change* change : changes) {
|
|
|
|
|
switch(change->getType()) {
|
2015-12-06 11:42:37 -03:00
|
|
|
case CHANGE_TILE: {
|
2023-01-27 17:41:20 -03:00
|
|
|
void** data = &change->data;
|
|
|
|
|
Tile* old_tile = reinterpret_cast<Tile*>(*data);
|
|
|
|
|
ASSERT(old_tile);
|
|
|
|
|
const Position& pos = old_tile->getPosition();
|
2016-09-29 19:24:45 -03:00
|
|
|
|
2015-12-06 11:42:37 -03:00
|
|
|
if(editor.IsLiveClient()) {
|
2023-01-27 17:41:20 -03:00
|
|
|
QTreeNode* node = map.getLeaf(pos.x, pos.y);
|
2023-01-29 23:32:19 -03:00
|
|
|
if(!node || !node->isVisible(pos.z > rme::MapGroundLayer)) {
|
2012-06-29 16:44:26 +02:00
|
|
|
// Delete all changes that affect tiles outside our view
|
2023-01-27 17:41:20 -03:00
|
|
|
change->clear();
|
2012-06-29 16:44:26 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
Tile* new_tile = map.swapTile(pos, old_tile);
|
2012-06-29 16:44:26 +02:00
|
|
|
|
|
|
|
|
// Update server side change list (for broadcast)
|
|
|
|
|
if(editor.IsLiveServer() && dirty_list)
|
|
|
|
|
dirty_list->AddPosition(pos.x, pos.y, pos.z);
|
|
|
|
|
|
|
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
if(old_tile->isSelected())
|
|
|
|
|
selection.addInternal(old_tile);
|
|
|
|
|
if(new_tile->isSelected())
|
|
|
|
|
selection.removeInternal(new_tile);
|
2012-06-29 16:44:26 +02:00
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
if(new_tile->getHouseID() != old_tile->getHouseID()) {
|
2012-06-29 16:44:26 +02:00
|
|
|
// oooooomggzzz we need to remove it from the appropriate house!
|
2023-01-27 17:41:20 -03:00
|
|
|
House* house = map.houses.getHouse(new_tile->getHouseID());
|
2015-12-06 11:42:37 -03:00
|
|
|
if(house) {
|
2023-01-27 17:41:20 -03:00
|
|
|
house->removeTile(new_tile);
|
2015-12-06 11:42:37 -03:00
|
|
|
} else {
|
2023-01-27 17:41:20 -03:00
|
|
|
new_tile->setHouse(nullptr);
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
house = map.houses.getHouse(old_tile->getHouseID());
|
2015-12-06 11:42:37 -03:00
|
|
|
if(house) {
|
2023-01-27 17:41:20 -03:00
|
|
|
house->addTile(old_tile);
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
2015-12-06 11:42:37 -03:00
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
if(old_tile->spawn) {
|
|
|
|
|
if(new_tile->spawn) {
|
|
|
|
|
if(*old_tile->spawn != *new_tile->spawn) {
|
|
|
|
|
map.removeSpawn(new_tile);
|
|
|
|
|
map.addSpawn(old_tile);
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
2015-12-06 11:42:37 -03:00
|
|
|
} else {
|
2023-01-27 17:41:20 -03:00
|
|
|
map.addSpawn(old_tile);
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
2023-01-27 17:41:20 -03:00
|
|
|
} else if(new_tile->spawn) {
|
|
|
|
|
map.removeSpawn(new_tile);
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
2023-01-27 17:41:20 -03:00
|
|
|
*data = new_tile;
|
2016-09-29 19:24:45 -03:00
|
|
|
|
2012-06-29 16:44:26 +02:00
|
|
|
// Update client dirty list
|
2015-12-06 11:42:37 -03:00
|
|
|
if(editor.IsLiveClient() && dirty_list && type != ACTION_REMOTE) {
|
2023-01-27 17:41:20 -03:00
|
|
|
dirty_list->AddChange(change);
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
2015-12-06 11:42:37 -03:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case CHANGE_MOVE_HOUSE_EXIT: {
|
2023-01-27 17:41:20 -03:00
|
|
|
HouseData* data = reinterpret_cast<HouseData*>(change->data);
|
|
|
|
|
ASSERT(data);
|
|
|
|
|
|
|
|
|
|
House* house = map.houses.getHouse(data->id);
|
|
|
|
|
if(house) {
|
|
|
|
|
const Position& oldpos = house->getExit();
|
|
|
|
|
house->setExit(data->position);
|
|
|
|
|
data->position = oldpos;
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
2015-12-06 11:42:37 -03:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case CHANGE_MOVE_WAYPOINT: {
|
2023-01-27 17:41:20 -03:00
|
|
|
WaypointData* data = reinterpret_cast<WaypointData*>(change->data);
|
|
|
|
|
ASSERT(data);
|
2012-06-29 16:44:26 +02:00
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
Waypoint* waypoint = map.waypoints.getWaypoint(data->id);
|
|
|
|
|
if(waypoint) {
|
|
|
|
|
TileLocation* old_tile = map.getTileL(waypoint->pos);
|
|
|
|
|
TileLocation* new_tile = map.getTileL(data->position);
|
2016-09-29 19:24:45 -03:00
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
if(data->position.isValid() && old_tile && old_tile->getWaypointCount() > 0)
|
|
|
|
|
old_tile->decreaseWaypointCount();
|
2012-06-29 16:44:26 +02:00
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
new_tile->increaseWaypointCount();
|
2012-06-29 16:44:26 +02:00
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
Position old_pos = waypoint->pos;
|
|
|
|
|
waypoint->pos = data->position;
|
|
|
|
|
data->position = old_pos;
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
2015-12-06 11:42:37 -03:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-29 16:44:26 +02:00
|
|
|
default:
|
2015-12-06 11:42:37 -03:00
|
|
|
break;
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-01-27 17:41:20 -03:00
|
|
|
|
2023-01-26 11:17:55 -03:00
|
|
|
selection.finish(Selection::INTERNAL);
|
2012-06-29 16:44:26 +02:00
|
|
|
commited = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BatchAction::BatchAction(Editor& editor, ActionIdentifier ident) :
|
|
|
|
|
editor(editor),
|
|
|
|
|
timestamp(0),
|
|
|
|
|
memory_size(0),
|
|
|
|
|
type(ident)
|
|
|
|
|
{
|
2015-12-06 11:42:37 -03:00
|
|
|
////
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BatchAction::~BatchAction()
|
|
|
|
|
{
|
2015-12-06 11:42:37 -03:00
|
|
|
for(Action* action : batch) {
|
2014-03-01 14:44:11 +01:00
|
|
|
delete action;
|
|
|
|
|
}
|
|
|
|
|
batch.clear();
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t BatchAction::memsize(bool recalc) const
|
|
|
|
|
{
|
|
|
|
|
// Expensive operation, only evaluate once (won't change anyways)
|
2015-12-06 11:42:37 -03:00
|
|
|
if(!recalc && memory_size > 0) {
|
2014-03-01 14:44:11 +01:00
|
|
|
return memory_size;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-28 22:34:25 +01:00
|
|
|
uint32_t mem = sizeof(*this);
|
2012-06-29 16:44:26 +02:00
|
|
|
mem += sizeof(Action*) * 3 * batch.size();
|
|
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
for(const Action* action : batch) {
|
2012-06-29 16:44:26 +02:00
|
|
|
#ifdef __USE_EXACT_MEMSIZE__
|
2014-03-01 14:44:11 +01:00
|
|
|
mem += action->memsize();
|
2012-06-29 16:44:26 +02:00
|
|
|
#else
|
|
|
|
|
// Less exact but MUCH faster
|
2014-03-01 14:44:11 +01:00
|
|
|
mem += action->approx_memsize();
|
2012-06-29 16:44:26 +02:00
|
|
|
#endif
|
|
|
|
|
}
|
2014-03-01 14:44:11 +01:00
|
|
|
|
2012-06-29 16:44:26 +02:00
|
|
|
const_cast<BatchAction*>(this)->memory_size = mem;
|
|
|
|
|
return mem;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
bool BatchAction::isNoSelection() const noexcept
|
|
|
|
|
{
|
|
|
|
|
return type != ACTION_SELECT && type != ACTION_UNSELECT;
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-29 16:44:26 +02:00
|
|
|
void BatchAction::addAction(Action* action)
|
|
|
|
|
{
|
2023-01-27 17:41:20 -03:00
|
|
|
if(!action) {
|
2012-06-29 16:44:26 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
if(action->empty() || !editor.CanEdit()) {
|
2012-06-29 16:44:26 +02:00
|
|
|
delete action;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
ASSERT(action->getType() == type);
|
|
|
|
|
|
2012-06-29 16:44:26 +02:00
|
|
|
batch.push_back(action);
|
2014-01-22 20:16:17 +01:00
|
|
|
timestamp = time(nullptr);
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BatchAction::addAndCommitAction(Action* action)
|
|
|
|
|
{
|
2023-01-27 17:41:20 -03:00
|
|
|
if(!action) {
|
2012-06-29 16:44:26 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2016-09-29 19:24:45 -03:00
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
if(!editor.CanEdit() || action->empty()) {
|
2012-06-29 16:44:26 +02:00
|
|
|
delete action;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-22 20:16:17 +01:00
|
|
|
action->commit(nullptr);
|
2012-06-29 16:44:26 +02:00
|
|
|
batch.push_back(action);
|
2014-01-22 20:16:17 +01:00
|
|
|
timestamp = time(nullptr);
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BatchAction::commit()
|
|
|
|
|
{
|
2015-12-06 11:42:37 -03:00
|
|
|
for(Action* action : batch) {
|
2023-01-27 17:41:20 -03:00
|
|
|
if(action && !action->isCommited()) {
|
2014-03-01 14:44:11 +01:00
|
|
|
action->commit(nullptr);
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
2014-03-01 14:44:11 +01:00
|
|
|
void BatchAction::undo()
|
|
|
|
|
{
|
2023-03-25 21:42:38 -03:00
|
|
|
for(Action* action : std::views::reverse(batch)) {
|
2014-03-01 14:44:11 +01:00
|
|
|
action->undo(nullptr);
|
|
|
|
|
}
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
2014-03-01 14:44:11 +01:00
|
|
|
void BatchAction::redo()
|
|
|
|
|
{
|
2015-12-06 11:42:37 -03:00
|
|
|
for(Action* action : batch) {
|
2014-03-01 14:44:11 +01:00
|
|
|
action->redo(nullptr);
|
|
|
|
|
}
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BatchAction::merge(BatchAction* other)
|
|
|
|
|
{
|
2014-03-01 14:44:11 +01:00
|
|
|
batch.insert(batch.end(), other->batch.begin(), other->batch.end());
|
2012-06-29 16:44:26 +02:00
|
|
|
other->batch.clear();
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-01 14:44:11 +01:00
|
|
|
ActionQueue::ActionQueue(Editor& editor) :
|
|
|
|
|
current(0), memory_size(0), editor(editor)
|
2012-06-29 16:44:26 +02:00
|
|
|
{
|
2015-12-06 11:42:37 -03:00
|
|
|
////
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ActionQueue::~ActionQueue()
|
|
|
|
|
{
|
2023-01-27 17:41:20 -03:00
|
|
|
for(BatchAction* batch : actions) {
|
|
|
|
|
delete batch;
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
2023-01-27 17:41:20 -03:00
|
|
|
actions.clear();
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
Action* ActionQueue::createAction(ActionIdentifier identifier) const
|
2012-06-29 16:44:26 +02:00
|
|
|
{
|
2023-01-27 17:41:20 -03:00
|
|
|
return new Action(editor, identifier);
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
Action* ActionQueue::createAction(BatchAction* batch) const
|
2012-06-29 16:44:26 +02:00
|
|
|
{
|
2023-01-27 17:41:20 -03:00
|
|
|
return new Action(editor, batch->getType());
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
BatchAction* ActionQueue::createBatch(ActionIdentifier identifier) const
|
2012-06-29 16:44:26 +02:00
|
|
|
{
|
2023-01-27 17:41:20 -03:00
|
|
|
return new BatchAction(editor, identifier);
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ActionQueue::resetTimer()
|
|
|
|
|
{
|
|
|
|
|
if(!actions.empty())
|
|
|
|
|
actions.back()->resetTimer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ActionQueue::addBatch(BatchAction* batch, int stacking_delay)
|
|
|
|
|
{
|
|
|
|
|
ASSERT(batch);
|
|
|
|
|
ASSERT(current <= actions.size());
|
|
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
if(batch->empty()) {
|
2012-06-29 16:44:26 +02:00
|
|
|
delete batch;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2015-12-06 11:42:37 -03:00
|
|
|
|
2012-06-29 16:44:26 +02:00
|
|
|
// Commit any uncommited actions...
|
|
|
|
|
batch->commit();
|
|
|
|
|
|
|
|
|
|
// Update title
|
2023-01-27 17:41:20 -03:00
|
|
|
if(batch->isNoSelection() && editor.getMap().doChange()) {
|
2016-09-29 20:30:54 -03:00
|
|
|
g_gui.UpdateTitle();
|
2023-01-27 17:41:20 -03:00
|
|
|
}
|
2016-09-29 19:24:45 -03:00
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
if(batch->getType() == ACTION_REMOTE) {
|
2012-06-29 16:44:26 +02:00
|
|
|
delete batch;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-06 11:42:37 -03:00
|
|
|
while(current != actions.size()) {
|
2012-06-29 16:44:26 +02:00
|
|
|
memory_size -= actions.back()->memsize();
|
|
|
|
|
BatchAction* todelete = actions.back();
|
|
|
|
|
actions.pop_back();
|
|
|
|
|
delete todelete;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-29 20:37:43 -03:00
|
|
|
while(memory_size > size_t(1024 * 1024 * g_settings.getInteger(Config::UNDO_MEM_SIZE)) && !actions.empty()) {
|
2012-06-29 16:44:26 +02:00
|
|
|
memory_size -= actions.front()->memsize();
|
|
|
|
|
delete actions.front();
|
|
|
|
|
actions.pop_front();
|
|
|
|
|
current--;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-29 20:37:43 -03:00
|
|
|
if(actions.size() > size_t(g_settings.getInteger(Config::UNDO_SIZE)) && !actions.empty()) {
|
2012-06-29 16:44:26 +02:00
|
|
|
memory_size -= actions.front()->memsize();
|
|
|
|
|
BatchAction* todelete = actions.front();
|
|
|
|
|
actions.pop_front();
|
|
|
|
|
delete todelete;
|
|
|
|
|
current--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
do {
|
2015-12-06 11:42:37 -03:00
|
|
|
if(!actions.empty()) {
|
2012-06-29 16:44:26 +02:00
|
|
|
BatchAction* lastAction = actions.back();
|
2016-09-29 20:37:43 -03:00
|
|
|
if(lastAction->type == batch->type && g_settings.getInteger(Config::GROUP_ACTIONS) && time(nullptr) - stacking_delay < lastAction->timestamp) {
|
2012-06-29 16:44:26 +02:00
|
|
|
lastAction->merge(batch);
|
2014-01-22 20:16:17 +01:00
|
|
|
lastAction->timestamp = time(nullptr);
|
2012-06-29 16:44:26 +02:00
|
|
|
memory_size -= lastAction->memsize();
|
|
|
|
|
memory_size += lastAction->memsize(true);
|
|
|
|
|
delete batch;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
memory_size += batch->memsize();
|
|
|
|
|
actions.push_back(batch);
|
2014-01-22 20:16:17 +01:00
|
|
|
batch->timestamp = time(nullptr);
|
2012-06-29 16:44:26 +02:00
|
|
|
current++;
|
|
|
|
|
} while(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ActionQueue::addAction(Action* action, int stacking_delay)
|
|
|
|
|
{
|
|
|
|
|
BatchAction* batch = createBatch(action->getType());
|
|
|
|
|
batch->addAndCommitAction(action);
|
2023-01-27 17:41:20 -03:00
|
|
|
if(batch->empty()) {
|
2012-06-29 16:44:26 +02:00
|
|
|
delete batch;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addBatch(batch, stacking_delay);
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
const BatchAction* ActionQueue::getAction(size_t index) const
|
|
|
|
|
{
|
|
|
|
|
if(index >= 0 && index < actions.size()) {
|
|
|
|
|
return actions.at(index);
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ActionQueue::generateLabels()
|
|
|
|
|
{
|
|
|
|
|
for(BatchAction* batch : actions) {
|
|
|
|
|
if(batch && batch->label.IsEmpty()) {
|
|
|
|
|
batch->label = createLabel(batch->getType());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ActionQueue::undo()
|
2012-06-29 16:44:26 +02:00
|
|
|
{
|
2015-12-06 11:42:37 -03:00
|
|
|
if(current > 0) {
|
2012-06-29 16:44:26 +02:00
|
|
|
current--;
|
2023-01-27 17:41:20 -03:00
|
|
|
BatchAction* batch = actions.at(current);
|
|
|
|
|
if(batch) {
|
|
|
|
|
batch->undo();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update title
|
|
|
|
|
if(batch->isNoSelection() && editor.getMap().doChange()) {
|
|
|
|
|
g_gui.UpdateTitle();
|
|
|
|
|
}
|
|
|
|
|
return true;
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
2023-01-27 17:41:20 -03:00
|
|
|
return false;
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
bool ActionQueue::redo()
|
2012-06-29 16:44:26 +02:00
|
|
|
{
|
2015-12-06 11:42:37 -03:00
|
|
|
if(current < actions.size()) {
|
2023-01-27 17:41:20 -03:00
|
|
|
BatchAction* batch = actions.at(current);
|
|
|
|
|
if(batch) {
|
|
|
|
|
batch->redo();
|
|
|
|
|
}
|
2012-06-29 16:44:26 +02:00
|
|
|
current++;
|
2023-01-27 17:41:20 -03:00
|
|
|
|
|
|
|
|
// Update title
|
|
|
|
|
if(batch->isNoSelection() && editor.getMap().doChange()) {
|
|
|
|
|
g_gui.UpdateTitle();
|
|
|
|
|
}
|
|
|
|
|
return true;
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
2023-01-27 17:41:20 -03:00
|
|
|
return false;
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
bool ActionQueue::hasChanges() const
|
2012-06-29 16:44:26 +02:00
|
|
|
{
|
2023-01-27 17:41:20 -03:00
|
|
|
for(const BatchAction* batch : actions) {
|
|
|
|
|
if(batch && !batch->empty() && batch->isNoSelection()) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
2023-01-27 17:41:20 -03:00
|
|
|
return false;
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
void ActionQueue::clear()
|
2012-06-29 16:44:26 +02:00
|
|
|
{
|
2023-01-27 17:41:20 -03:00
|
|
|
for(BatchAction* batch : actions) {
|
|
|
|
|
delete batch;
|
|
|
|
|
}
|
|
|
|
|
actions.clear();
|
|
|
|
|
current = 0;
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-27 17:41:20 -03:00
|
|
|
wxString ActionQueue::createLabel(ActionIdentifier type)
|
|
|
|
|
{
|
|
|
|
|
switch (type) {
|
|
|
|
|
case ACTION_MOVE: return "Move";
|
|
|
|
|
case ACTION_SELECT: return "Select";
|
|
|
|
|
case ACTION_UNSELECT: return "Unselect";
|
|
|
|
|
case ACTION_DELETE_TILES: return "Delete";
|
|
|
|
|
case ACTION_CUT_TILES: return "Cut";
|
|
|
|
|
case ACTION_PASTE_TILES: return "Paste";
|
|
|
|
|
case ACTION_RANDOMIZE: return "Randomize";
|
|
|
|
|
case ACTION_BORDERIZE: return "Borderize";
|
|
|
|
|
case ACTION_DRAW: return "Draw";
|
|
|
|
|
case ACTION_ERASE: return "Erase";
|
|
|
|
|
case ACTION_SWITCHDOOR: return "Switch Door";
|
|
|
|
|
case ACTION_ROTATE_ITEM: return "Rotate Item";
|
|
|
|
|
case ACTION_REPLACE_ITEMS: return "Replace";
|
|
|
|
|
case ACTION_CHANGE_PROPERTIES: return "Change Properties";
|
|
|
|
|
default: return wxEmptyString;
|
|
|
|
|
}
|
2012-06-29 16:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DirtyList::AddPosition(int x, int y, int z)
|
|
|
|
|
{
|
|
|
|
|
uint32_t m = ((x >> 2) << 18) | ((y >> 2) << 4);
|
|
|
|
|
ValueType fi = {m, 0};
|
|
|
|
|
SetType::iterator s = iset.find(fi);
|
2015-12-06 11:42:37 -03:00
|
|
|
if(s != iset.end()) {
|
2012-06-29 16:44:26 +02:00
|
|
|
ValueType v = *s;
|
|
|
|
|
iset.erase(s);
|
|
|
|
|
v.floors = (1 << z) | v.floors;
|
|
|
|
|
iset.insert(v);
|
2015-12-06 11:42:37 -03:00
|
|
|
} else {
|
2014-01-23 21:06:52 -02:00
|
|
|
ValueType v = {m, (uint32_t)(1 << z)};
|
2012-06-29 16:44:26 +02:00
|
|
|
iset.insert(v);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DirtyList::AddChange(Change* c)
|
|
|
|
|
{
|
|
|
|
|
ichanges.push_back(c);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DirtyList::SetType& DirtyList::GetPosList()
|
|
|
|
|
{
|
|
|
|
|
return iset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ChangeList& DirtyList::GetChanges()
|
|
|
|
|
{
|
|
|
|
|
return ichanges;
|
|
|
|
|
}
|