copybuffer.cpp

main 311 lines · 8.4 KB Raw
Alex Alex Commit Map Editor 18/10/2026 23:40
1//////////////////////////////////////////////////////////////////////
2// This file is part of Remere's Map Editor
3//////////////////////////////////////////////////////////////////////
4// Remere's Map Editor is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Remere's Map Editor is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16//////////////////////////////////////////////////////////////////////
17
18#include "main.h"
19
20#include "copybuffer.h"
21#include "editor.h"
22#include "gui.h"
23#include "creature.h"
24#include "minimap_window.h"
25
26CopyBuffer::CopyBuffer() :
27 tiles(newd BaseMap()) {
28 ;
29}
30
31size_t CopyBuffer::GetTileCount() {
32 return tiles ? (size_t)tiles->size() : 0;
33}
34
35BaseMap& CopyBuffer::getBufferMap() {
36 ASSERT(tiles);
37 return *tiles;
38}
39
40CopyBuffer::~CopyBuffer() {
41 clear();
42}
43
44Position CopyBuffer::getPosition() const {
45 ASSERT(tiles);
46 return copyPos;
47}
48
49void CopyBuffer::clear() {
50 delete tiles;
51 tiles = nullptr;
52}
53
54void CopyBuffer::copy(Editor& editor, int floor) {
55 if (editor.selection.size() == 0) {
56 g_gui.SetStatusText("No tiles to copy.");
57 return;
58 }
59
60 clear();
61 tiles = newd BaseMap();
62
63 int tile_count = 0;
64 int item_count = 0;
65 copyPos = Position(0xFFFF, 0xFFFF, floor);
66
67 for (TileSet::iterator it = editor.selection.begin(); it != editor.selection.end(); ++it) {
68 ++tile_count;
69
70 Tile* tile = *it;
71 TileLocation* newlocation = tiles->createTileL(tile->getPosition());
72 Tile* copied_tile = tiles->allocator(newlocation);
73
74 if (tile->ground && tile->ground->isSelected()) {
75 copied_tile->house_id = tile->house_id;
76 copied_tile->setMapFlags(tile->getMapFlags());
77 copied_tile->setZoneIds(tile);
78 }
79
80 ItemVector tile_selection = tile->getSelectedItems();
81 for (ItemVector::iterator iit = tile_selection.begin(); iit != tile_selection.end(); ++iit) {
82 ++item_count;
83 // Copy items to copybuffer
84 copied_tile->addItem((*iit)->deepCopy());
85 }
86
87 if (tile->creature && tile->creature->isSelected()) {
88 copied_tile->creature = tile->creature->deepCopy();
89 }
90 if (tile->spawn && tile->spawn->isSelected()) {
91 copied_tile->spawn = tile->spawn->deepCopy();
92 }
93
94 tiles->setTile(copied_tile);
95
96 if (copied_tile->getX() < copyPos.x) {
97 copyPos.x = copied_tile->getX();
98 }
99
100 if (copied_tile->getY() < copyPos.y) {
101 copyPos.y = copied_tile->getY();
102 }
103 }
104
105 std::ostringstream ss;
106 ss << "Copied " << tile_count << " tile" << (tile_count > 1 ? "s" : "") << " (" << item_count << " item" << (item_count > 1 ? "s" : "") << ")";
107 g_gui.SetStatusText(wxstr(ss.str()));
108}
109
110void CopyBuffer::cut(Editor& editor, int floor) {
111 if (editor.selection.size() == 0) {
112 g_gui.SetStatusText("No tiles to cut.");
113 return;
114 }
115
116 clear();
117 tiles = newd BaseMap();
118
119 int tile_count = 0;
120 int item_count = 0;
121 copyPos = Position(0xFFFF, 0xFFFF, floor);
122
123 BatchAction* batch = editor.actionQueue->createBatch(ACTION_CUT_TILES);
124 Action* action = editor.actionQueue->createAction(batch);
125
126 PositionList tilestoborder;
127
128 for (TileSet::iterator it = editor.selection.begin(); it != editor.selection.end(); ++it) {
129 tile_count++;
130
131 Tile* tile = *it;
132 Tile* newtile = tile->deepCopy(editor.map);
133 Tile* copied_tile = tiles->allocator(tile->getLocation());
134
135 if (tile->ground && tile->ground->isSelected()) {
136 copied_tile->house_id = newtile->house_id;
137 newtile->house_id = 0;
138 copied_tile->setZoneIds(tile);
139 copied_tile->setMapFlags(tile->getMapFlags());
140 newtile->setMapFlags(TILESTATE_NONE);
141 newtile->clearZoneId();
142 }
143
144 ItemVector tile_selection = newtile->popSelectedItems();
145 for (ItemVector::iterator iit = tile_selection.begin(); iit != tile_selection.end(); ++iit) {
146 item_count++;
147 // Add items to copybuffer
148 copied_tile->addItem(*iit);
149 }
150
151 if (newtile->creature && newtile->creature->isSelected()) {
152 copied_tile->creature = newtile->creature;
153 newtile->creature = nullptr;
154 }
155
156 if (newtile->spawn && newtile->spawn->isSelected()) {
157 copied_tile->spawn = newtile->spawn;
158 newtile->spawn = nullptr;
159 }
160
161 tiles->setTile(copied_tile->getPosition(), copied_tile);
162
163 if (copied_tile->getX() < copyPos.x) {
164 copyPos.x = copied_tile->getX();
165 }
166
167 if (copied_tile->getY() < copyPos.y) {
168 copyPos.y = copied_tile->getY();
169 }
170
171 if (g_settings.getInteger(Config::USE_AUTOMAGIC)) {
172 for (int y = -1; y <= 1; y++) {
173 for (int x = -1; x <= 1; x++) {
174 tilestoborder.push_back(Position(tile->getX() + x, tile->getY() + y, tile->getZ()));
175 }
176 }
177 }
178 action->addChange(newd Change(newtile));
179 }
180
181 batch->addAndCommitAction(action);
182
183 // Remove duplicates
184 tilestoborder.sort();
185 tilestoborder.unique();
186
187 if (g_settings.getInteger(Config::USE_AUTOMAGIC)) {
188 action = editor.actionQueue->createAction(batch);
189 for (PositionList::iterator it = tilestoborder.begin(); it != tilestoborder.end(); ++it) {
190 TileLocation* location = editor.map.createTileL(*it);
191 if (location->get()) {
192 Tile* new_tile = location->get()->deepCopy(editor.map);
193 new_tile->borderize(&editor.map);
194 new_tile->wallize(&editor.map);
195 action->addChange(newd Change(new_tile));
196 } else {
197 Tile* new_tile = editor.map.allocator(location);
198 new_tile->borderize(&editor.map);
199 if (new_tile->size()) {
200 action->addChange(newd Change(new_tile));
201 } else {
202 delete new_tile;
203 }
204 }
205 }
206
207 batch->addAndCommitAction(action);
208 }
209
210 editor.addBatch(batch);
211 std::stringstream ss;
212 ss << "Cut out " << tile_count << " tile" << (tile_count > 1 ? "s" : "") << " (" << item_count << " item" << (item_count > 1 ? "s" : "") << ")";
213 g_gui.SetStatusText(wxstr(ss.str()));
214}
215
216void CopyBuffer::paste(Editor& editor, const Position& toPosition) {
217 if (!tiles) {
218 return;
219 }
220
221 BatchAction* batchAction = editor.actionQueue->createBatch(ACTION_PASTE_TILES);
222 Action* action = editor.actionQueue->createAction(batchAction);
223
224 // Track modified positions for minimap update
225 PositionVector modifiedPositions;
226
227 // Original paste logic
228 for (MapIterator it = tiles->begin(); it != tiles->end(); ++it) {
229 Tile* buffer_tile = (*it)->get();
230 Position pos = buffer_tile->getPosition() - copyPos + toPosition;
231
232 if (!pos.isValid()) {
233 continue;
234 }
235
236 // Add position for minimap update
237 modifiedPositions.push_back(pos);
238
239 // Add surrounding positions for border updates
240 for(int dx = -1; dx <= 1; dx++) {
241 for(int dy = -1; dy <= 1; dy++) {
242 Position borderPos(pos.x + dx, pos.y + dy, pos.z);
243 if(borderPos.isValid()) {
244 modifiedPositions.push_back(borderPos);
245 }
246 }
247 }
248
249 TileLocation* location = editor.map.createTileL(pos);
250 Tile* copy_tile = buffer_tile->deepCopy(editor.map);
251 Tile* old_dest_tile = location->get();
252 Tile* new_dest_tile = nullptr;
253 copy_tile->setLocation(location);
254
255 if (g_settings.getInteger(Config::MERGE_PASTE) || !copy_tile->ground) {
256 if (old_dest_tile) {
257 new_dest_tile = old_dest_tile->deepCopy(editor.map);
258 } else {
259 new_dest_tile = editor.map.allocator(location);
260 }
261 new_dest_tile->merge(copy_tile);
262 delete copy_tile;
263 } else {
264 new_dest_tile = copy_tile;
265 }
266
267 action->addChange(newd Change(new_dest_tile));
268 }
269
270 batchAction->addAndCommitAction(action);
271
272 if (g_settings.getInteger(Config::USE_AUTOMAGIC) && g_settings.getInteger(Config::BORDERIZE_PASTE)) {
273 action = editor.actionQueue->createAction(batchAction);
274 TileList borderize_tiles;
275 Map& map = editor.map;
276
277 for (const Position& pos : modifiedPositions) {
278 Tile* tile = map.getTile(pos);
279 if (tile) {
280 borderize_tiles.push_back(tile);
281 }
282 }
283
284 // Remove duplicates
285 borderize_tiles.sort();
286 borderize_tiles.unique();
287
288 for (Tile* tile : borderize_tiles) {
289 if (tile) {
290 Tile* newTile = tile->deepCopy(editor.map);
291 newTile->borderize(&map);
292 newTile->wallize(&map);
293 action->addChange(newd Change(newTile));
294 }
295 }
296
297 batchAction->addAndCommitAction(action);
298 }
299
300 editor.addBatch(batchAction);
301
302 // Update minimap with modified positions
303 if (g_gui.minimap) {
304 g_gui.minimap->UpdateDrawnTiles(modifiedPositions);
305 }
306}
307
308bool CopyBuffer::canPaste() const {
309 return tiles && tiles->size() != 0;
310}
311
Top