The Battle for Wesnoth  1.15.12+dev
editor_map.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2018 by Tomasz Sniatowski <kailoran@gmail.com>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 #define GETTEXT_DOMAIN "wesnoth-editor"
15 
18 
19 #include "display.hpp"
20 #include "formula/string_utils.hpp"
21 #include "gettext.hpp"
22 #include "map/exception.hpp"
23 #include "map/label.hpp"
24 #include "wml_exception.hpp"
25 
26 namespace editor {
27 
28 editor_map_load_exception wrap_exc(const char* type, const std::string& e_msg, const std::string& filename)
29 {
30  WRN_ED << type << " error in load map " << filename << ": " << e_msg << std::endl;
31  utils::string_map symbols;
32  symbols["type"] = type;
33  const char* error_msg = "There was an error ($type) while loading the file:";
34  std::string msg = VGETTEXT(error_msg, symbols);
35  msg += "\n";
36  msg += e_msg;
37  return editor_map_load_exception(filename, msg);
38 }
39 
41  : gamemap("")
42  , selection_()
43 {
44 }
45 
46 editor_map::editor_map(const std::string& data)
47  : gamemap(data)
48  , selection_()
49 {
50  sanity_check();
51 }
52 
53 editor_map editor_map::from_string(const std::string& data)
54 {
55  try {
56  return editor_map(data);
57  } catch (const incorrect_map_format_error& e) {
58  throw wrap_exc("format", e.message, "");
59  } catch (const wml_exception& e) {
60  throw wrap_exc("wml", e.user_message, "");
61  } catch (const config::error& e) {
62  throw wrap_exc("config", e.message, "");
63  }
64 }
65 
66 editor_map::editor_map(std::size_t width, std::size_t height, const t_translation::terrain_code & filler)
67  : gamemap(t_translation::write_game_map(t_translation::ter_map(width + 2, height + 2, filler)))
68  , selection_()
69 {
70  sanity_check();
71 }
72 
74  : gamemap(map)
75  , selection_()
76 {
77  sanity_check();
78 }
79 
81 {
82 }
83 
85 {
86  int errors = 0;
87  if (total_width() != tiles().w) {
88  ERR_ED << "total_width is " << total_width() << " but tiles().size() is " << tiles().w << std::endl;
89  ++errors;
90  }
91  if (total_height() != tiles().h) {
92  ERR_ED << "total_height is " << total_height() << " but tiles()[0].size() is " << tiles().h << std::endl;
93  ++errors;
94  }
95  if (w() + 2 * border_size() != total_width()) {
96  ERR_ED << "h is " << h() << " and border_size is " << border_size() << " but total_width is " << total_width() << std::endl;
97  ++errors;
98  }
99  if (h() + 2 * border_size() != total_height()) {
100  ERR_ED << "w is " << w() << " and border_size is " << border_size() << " but total_height is " << total_height() << std::endl;
101  ++errors;
102  }
103  for (const map_location& loc : selection_) {
104  if (!on_board_with_border(loc)) {
105  ERR_ED << "Off-map tile in selection: " << loc << std::endl;
106  }
107  }
108  if (errors) {
110  }
111 }
112 
113 std::set<map_location> editor_map::get_contiguous_terrain_tiles(const map_location& start) const
114 {
115  t_translation::terrain_code terrain = get_terrain(start);
116  std::set<map_location> result;
117  std::deque<map_location> queue;
118  result.insert(start);
119  queue.push_back(start);
120  //this is basically a breadth-first search along adjacent hexes
121  do {
122  for(const map_location& adj : get_adjacent_tiles(queue.front())) {
123  if (on_board_with_border(adj) && get_terrain(adj) == terrain
124  && result.find(adj) == result.end()) {
125  result.insert(adj);
126  queue.push_back(adj);
127  }
128  }
129  queue.pop_front();
130  } while (!queue.empty());
131  return result;
132 }
133 
135 {
136  std::set<map_location> label_locs;
137  std::string label;
138 
139 
140  for (const auto& pair : special_locations().left) {
141 
142  bool is_number = std::find_if(pair.first.begin(), pair.first.end(), [](char c) { return !std::isdigit(c); }) == pair.first.end();
143  if (is_number) {
144  label = VGETTEXT("Player $side_num", utils::string_map{ { "side_num", pair.first } });
145  }
146  else {
147  label = pair.first;
148  }
149 
150  disp.labels().set_label(pair.second, label);
151  label_locs.insert(pair.second);
152  }
153  return label_locs;
154 }
155 
157 {
158  return selection_.find(loc) != selection_.end();
159 }
160 
162 {
163  return on_board_with_border(loc) ? selection_.insert(loc).second : false;
164 }
165 
166 bool editor_map::set_selection(const std::set<map_location>& area)
167 {
168  clear_selection();
169  for (const map_location& loc : area) {
170  if (!add_to_selection(loc))
171  return false;
172  }
173  return true;
174 }
175 
177 {
178  return selection_.erase(loc) != 0;
179 }
180 
182 {
183  selection_.clear();
184 }
185 
187 {
188  std::set<map_location> new_selection;
189  for (int x = -1; x < w() + 1; ++x) {
190  for (int y = -1; y < h() + 1; ++y) {
191  if (selection_.find(map_location(x, y)) == selection_.end()) {
192  new_selection.emplace(x, y);
193  }
194  }
195  }
196  selection_.swap(new_selection);
197 }
198 
200 {
201  clear_selection();
203 }
204 
206 {
207  LOG_ED << selection_.size() << " " << total_width() * total_height() << "\n";
208  return static_cast<int>(selection_.size()) == total_width() * total_height();
209 }
210 
211 void editor_map::resize(int width, int height, int x_offset, int y_offset,
212  const t_translation::terrain_code & filler)
213 {
214  int old_w = w();
215  int old_h = h();
216  if (old_w == width && old_h == height && x_offset == 0 && y_offset == 0) {
217  return;
218  }
219 
220  // Determine the amount of resizing is required
221  const int left_resize = -x_offset;
222  const int right_resize = (width - old_w) + x_offset;
223  const int top_resize = -y_offset;
224  const int bottom_resize = (height - old_h) + y_offset;
225 
226  if(right_resize > 0) {
227  expand_right(right_resize, filler);
228  } else if(right_resize < 0) {
229  shrink_right(-right_resize);
230  }
231  if(bottom_resize > 0) {
232  expand_bottom(bottom_resize, filler);
233  } else if(bottom_resize < 0) {
234  shrink_bottom(-bottom_resize);
235  }
236  if(left_resize > 0) {
237  expand_left(left_resize, filler);
238  } else if(left_resize < 0) {
239  shrink_left(-left_resize);
240  }
241  if(top_resize > 0) {
242  expand_top(top_resize, filler);
243  } else if(top_resize < 0) {
244  shrink_top(-top_resize);
245  }
246 
247  // fix the starting positions
248  if(x_offset || y_offset) {
249  for (auto it = special_locations().left.begin(); it != special_locations().left.end(); ++it) {
250  special_locations().left.modify_data(it, [=](t_translation::coordinate & loc) { loc.add(-x_offset, -y_offset); });
251  }
252  }
253 
254  villages_.clear();
255 
256  //
257  // NOTE: I'm not sure how inefficient it is to check every loc for its village-ness as
258  // opposed to operating on the villages_ vector itself and figuring out how to handle
259  // villages on the map border. Essentially, it's possible to simply remove all members
260  // from villages_ that are no longer on the map after a resize (including those that
261  // land on a border), but that doesn't account for villages that were *on* the border
262  // prior to resizing. Those should be included. As a catch-all fix, I just check every
263  // hex. It's possible that any more complex shenanigans would be even more inefficient.
264  //
265  // -- vultraz, 2018-02-25
266  //
267  for_each_loc([this](const map_location& loc) {
268  if(is_village(loc)) {
269  villages_.push_back(loc);
270  }
271  });
272 
273  sanity_check();
274 }
275 
276 gamemap editor_map::mask_to(const gamemap& target) const
277 {
278  if (target.w() != w() || target.h() != h()) {
279  throw editor_action_exception(_("The size of the target map is different from the current map"));
280  }
281  gamemap mask(target);
282  map_location iter;
283  for (iter.x = -border_size(); iter.x < w() + border_size(); ++iter.x) {
284  for (iter.y = -border_size(); iter.y < h() + border_size(); ++iter.y) {
285  if (target.get_terrain(iter) == get_terrain(iter)) {
287  }
288  }
289  }
290  return mask;
291 }
292 
293 bool editor_map::same_size_as(const gamemap& other) const
294 {
295  return h() == other.h()
296  && w() == other.w();
297 }
298 
300 {
301  t_translation::ter_map tiles_new(tiles().w + count, tiles().h);
302  for (int x = 0, x_end = tiles().w; x != x_end; ++x) {
303  for (int y = 0, y_end = tiles().h; y != y_end; ++y) {
304  tiles_new.get(x, y) = tiles().get(x, y);
305  }
306  }
307  for (int x = tiles().w, x_end = tiles().w + count; x != x_end; ++x) {
308  for (int y = 0, y_end = tiles().h; y != y_end; ++y) {
309  tiles_new.get(x, y) = filler == t_translation::NONE_TERRAIN ? tiles().get(tiles().w - 1, y) : filler;
310  }
311  }
312  tiles() = std::move(tiles_new);
313 }
314 
316 {
317  t_translation::ter_map tiles_new(tiles().w + count, tiles().h);
318  for (int x = 0, x_end = tiles().w; x != x_end; ++x) {
319  for (int y = 0, y_end = tiles().h; y != y_end; ++y) {
320  tiles_new.get(x + count, y) = tiles().get(x, y);
321  }
322  }
323  for (int x = 0, x_end = count; x != x_end; ++x) {
324  for (int y = 0, y_end = tiles().h; y != y_end; ++y) {
325  tiles_new.get(x, y) = filler == t_translation::NONE_TERRAIN ? tiles().get(0, y) : filler;
326  }
327  }
328  tiles() = std::move(tiles_new);
329 }
330 
332 {
333  t_translation::ter_map tiles_new(tiles().w, tiles().h + count);
334  for (int x = 0, x_end = tiles().w; x != x_end; ++x) {
335  for (int y = 0, y_end = tiles().h; y != y_end; ++y) {
336  tiles_new.get(x, y + count) = tiles().get(x, y);
337  }
338  }
339  for (int x = 0, x_end = tiles().w; x != x_end; ++x) {
340  for (int y = 0, y_end = count; y != y_end; ++y) {
341  tiles_new.get(x, y) = filler == t_translation::NONE_TERRAIN ? tiles().get(x, 0) : filler;
342  }
343  }
344  tiles() = std::move(tiles_new);
345 }
346 
348 {
349  t_translation::ter_map tiles_new(tiles().w, tiles().h + count);
350  for (int x = 0, x_end = tiles().w; x != x_end; ++x) {
351  for (int y = 0, y_end = tiles().h; y != y_end; ++y) {
352  tiles_new.get(x, y) = tiles().get(x, y);
353  }
354  }
355  for (int x = 0, x_end = tiles().w; x != x_end; ++x) {
356  for (int y = tiles().h, y_end = tiles().h + count; y != y_end; ++y) {
357  tiles_new.get(x, y) = filler == t_translation::NONE_TERRAIN ? tiles().get(x, tiles().h - 1) : filler;
358  }
359  }
360  tiles() = std::move(tiles_new);
361 }
362 
364 {
365  if(count < 0 || count > tiles().w) {
367  }
368  t_translation::ter_map tiles_new(tiles().w - count, tiles().h);
369  for (int x = 0, x_end = tiles_new.w; x != x_end; ++x) {
370  for (int y = 0, y_end = tiles_new.h; y != y_end; ++y) {
371  tiles_new.get(x, y) = tiles().get(x, y);
372  }
373  }
374  tiles() = std::move(tiles_new);
375 }
376 
377 void editor_map::shrink_left(int count)
378 {
379  if (count < 0 || count > tiles().w) {
381  }
382  t_translation::ter_map tiles_new(tiles().w - count, tiles().h);
383  for (int x = 0, x_end = tiles_new.w; x != x_end; ++x) {
384  for (int y = 0, y_end = tiles_new.h; y != y_end; ++y) {
385  tiles_new.get(x, y) = tiles().get(x + count, y);
386  }
387  }
388  tiles() = std::move(tiles_new);
389 }
390 
391 void editor_map::shrink_top(int count)
392 {
393  if (count < 0 || count > tiles().h) {
395  }
396  t_translation::ter_map tiles_new(tiles().w, tiles().h - count);
397  for (int x = 0, x_end = tiles_new.w; x != x_end; ++x) {
398  for (int y = 0, y_end = tiles_new.h; y != y_end; ++y) {
399  tiles_new.get(x, y) = tiles().get(x, y + count);
400  }
401  }
402  tiles() = std::move(tiles_new);
403 }
404 
406 {
407  if (count < 0 || count > tiles().h) {
409  }
410  t_translation::ter_map tiles_new(tiles().w, tiles().h - count);
411  for (int x = 0, x_end = tiles_new.w; x != x_end; ++x) {
412  for (int y = 0, y_end = tiles_new.h; y != y_end; ++y) {
413  tiles_new.get(x, y) = tiles().get(x, y);
414  }
415  }
416  tiles() = std::move(tiles_new);
417 }
418 
419 
420 
421 } //end namespace editor
void expand_bottom(int count, const t_translation::terrain_code &filler)
Definition: editor_map.cpp:347
const terrain_code NONE_TERRAIN
Definition: translation.hpp:58
location_map & special_locations()
Definition: map.hpp:89
static editor_map from_string(const std::string &data)
Wrapper around editor_map(cfg, data) that catches possible exceptions and wraps them in a editor_map_...
Definition: editor_map.cpp:53
std::map< std::string, t_string > string_map
void get_adjacent_tiles(const map_location &a, map_location *res)
Function which, given a location, will place all adjacent locations in res.
Definition: location.cpp:474
std::vector< map_location > villages_
Definition: map.hpp:256
void set_terrain(const map_location &loc, const terrain_code &terrain, const terrain_type_data::merge_mode mode=terrain_type_data::BOTH, bool replace_if_failed=false) override
Clobbers over the terrain at location &#39;loc&#39;, with the given terrain.
Definition: map.cpp:392
std::set< map_location > selection_
The selected hexes.
Definition: editor_map.hpp:203
std::set< map_location > set_starting_position_labels(display &disp)
Set labels for staring positions in the given display object.
Definition: editor_map.cpp:134
void shrink_top(int count)
Definition: editor_map.cpp:391
Add a special kind of assert to validate whether the input from WML doesn&#39;t contain any problems that...
bool in_selection(const map_location &loc) const
Definition: editor_map.cpp:156
#define LOG_ED
std::string user_message
The message for the user explaining what went wrong.
bool same_size_as(const gamemap &other) const
A precondition to several map operations.
Definition: editor_map.cpp:293
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:49
void sanity_check()
Debugging aid.
Definition: editor_map.cpp:84
void for_each_loc(const F &f) const
Definition: map.hpp:135
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
Base class for editor actions.
void resize(int width, int height, int x_offset, int y_offset, const t_translation::terrain_code &filler=t_translation::NONE_TERRAIN)
Resize the map.
Definition: editor_map.cpp:211
static std::string _(const char *str)
Definition: gettext.hpp:92
~editor_map()
editor_map destructor
Definition: editor_map.cpp:80
editor_map_load_exception wrap_exc(const char *type, const std::string &e_msg, const std::string &filename)
Exception wrapping utility.
Definition: editor_map.cpp:28
#define WRN_ED
terrain_map & tiles()
Definition: map.hpp:156
void add(int x_diff, int y_diff)
Definition: location.hpp:158
int w() const
Effective map width.
Definition: map.hpp:49
const terrain_code FOGGED
const terrain_label * set_label(const map_location &loc, const t_string &text, const int creator=-1, const std::string &team="", const color_t color=font::NORMAL_COLOR, const bool visible_in_fog=true, const bool visible_in_shroud=false, const bool immutable=false, const std::string &category="", const t_string &tooltip="")
Definition: label.cpp:146
void expand_left(int count, const t_translation::terrain_code &filler)
Definition: editor_map.cpp:315
void invert_selection()
Invert the selection, i.e.
Definition: editor_map.cpp:186
terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition: map.cpp:297
bool on_board_with_border(const map_location &loc) const
Definition: map.cpp:385
Encapsulates the map of the game.
Definition: map.hpp:170
std::set< map_location > get_contiguous_terrain_tiles(const map_location &start) const
Get a contiguous set of tiles having the same terrain as the starting location.
Definition: editor_map.cpp:113
map_display and display: classes which take care of displaying the map and game-data on the screen...
editor_map()
Empty map constructor.
Definition: editor_map.cpp:40
Manage the empty-palette in the editor.
Definition: action.cpp:29
void shrink_left(int count)
Definition: editor_map.cpp:377
gamemap mask_to(const gamemap &target) const
A sort-of diff operation returning a mask that, when applied to the current editor_map, will transform it into the target map.
Definition: editor_map.cpp:276
bool add_to_selection(const map_location &loc)
Add a location to the selection.
Definition: editor_map.cpp:161
Encapsulates the map of the game.
Definition: location.hpp:37
Helper class, don&#39;t construct this directly.
int total_width() const
Real width of the map, including borders.
Definition: map.hpp:58
This class adds extra editor-specific functionality to a normal gamemap.
Definition: editor_map.hpp:69
bool everything_selected() const
Definition: editor_map.cpp:205
void expand_right(int count, const t_translation::terrain_code &filler)
Definition: editor_map.cpp:299
void clear_selection()
Clear the selection.
Definition: editor_map.cpp:181
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
terrain_code & get(int x, int y)
Definition: translation.hpp:89
int total_height() const
Real height of the map, including borders.
Definition: map.hpp:61
std::string write_game_map(const ter_map &map, const starting_positions &starting_positions, coordinate border_offset)
Write a gamemap in to a vector string.
void select_all()
Select all map hexes.
Definition: editor_map.cpp:199
bool is_village(const map_location &loc) const
Definition: map.cpp:65
lu_byte left
Definition: lparser.cpp:1226
#define ERR_ED
void expand_top(int count, const t_translation::terrain_code &filler)
Definition: editor_map.cpp:331
int border_size() const
Size of the map border.
Definition: map.hpp:55
std::string message
Definition: exceptions.hpp:29
EXIT_STATUS start(const std::string &filename, bool take_screenshot, const std::string &screenshot_filename)
Main interface for launching the editor from the title screen.
Definition: editor_main.cpp:28
map_labels & labels()
Definition: display.cpp:2535
#define e
mock_char c
int h() const
Effective map height.
Definition: map.hpp:52
void shrink_right(int count)
Definition: editor_map.cpp:363
bool set_selection(const std::set< map_location > &area)
Select the given area.
Definition: editor_map.cpp:166
bool remove_from_selection(const map_location &loc)
Remove a location to the selection.
Definition: editor_map.cpp:176
void shrink_bottom(int count)
Definition: editor_map.cpp:405