The Battle for Wesnoth  1.15.0-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 http://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 #include "terrain/type_data.hpp"
27 
28 namespace editor {
29 
30 editor_map_load_exception wrap_exc(const char* type, const std::string& e_msg, const std::string& filename)
31 {
32  WRN_ED << type << " error in load map " << filename << ": " << e_msg << std::endl;
33  utils::string_map symbols;
34  symbols["type"] = type;
35  const char* error_msg = "There was an error ($type) while loading the file:";
36  std::string msg = VGETTEXT(error_msg, symbols);
37  msg += "\n";
38  msg += e_msg;
39  return editor_map_load_exception(filename, msg);
40 }
41 
42 editor_map::editor_map(const config& terrain_cfg)
43  : gamemap(std::make_shared<terrain_type_data>(terrain_cfg), "")
44  , selection_()
45 {
46 }
47 
48 editor_map::editor_map(const config& terrain_cfg, const std::string& data)
49  : gamemap(std::make_shared<terrain_type_data>(terrain_cfg), data)
50  , selection_()
51 {
52  sanity_check();
53 }
54 
55 editor_map editor_map::from_string(const config& terrain_cfg, const std::string& data)
56 {
57  try {
58  return editor_map(terrain_cfg, data);
59  } catch (const incorrect_map_format_error& e) {
60  throw wrap_exc("format", e.message, "");
61  } catch (const wml_exception& e) {
62  throw wrap_exc("wml", e.user_message, "");
63  } catch (const config::error& e) {
64  throw wrap_exc("config", e.message, "");
65  }
66 }
67 
68 editor_map::editor_map(const config& terrain_cfg, std::size_t width, std::size_t height, const t_translation::terrain_code & filler)
69  : gamemap(std::make_shared<terrain_type_data>(terrain_cfg), t_translation::write_game_map(t_translation::ter_map(width + 2, height + 2, filler)))
70  , selection_()
71 {
72  sanity_check();
73 }
74 
76  : gamemap(map)
77  , selection_()
78 {
79  sanity_check();
80 }
81 
83 {
84 }
85 
87 {
88  int errors = 0;
89  if (total_width() != tiles_.w) {
90  ERR_ED << "total_width is " << total_width() << " but tiles_.size() is " << tiles_.w << std::endl;
91  ++errors;
92  }
93  if (total_height() != tiles_.h) {
94  ERR_ED << "total_height is " << total_height() << " but tiles_[0].size() is " << tiles_.h << std::endl;
95  ++errors;
96  }
97  if (w() + 2 * border_size() != total_width()) {
98  ERR_ED << "h is " << h_ << " and border_size is " << border_size() << " but total_width is " << total_width() << std::endl;
99  ++errors;
100  }
101  if (h() + 2 * border_size() != total_height()) {
102  ERR_ED << "w is " << w_ << " and border_size is " << border_size() << " but total_height is " << total_height() << std::endl;
103  ++errors;
104  }
105  for (const map_location& loc : selection_) {
106  if (!on_board_with_border(loc)) {
107  ERR_ED << "Off-map tile in selection: " << loc << std::endl;
108  }
109  }
110  if (errors) {
112  }
113 }
114 
115 std::set<map_location> editor_map::get_contiguous_terrain_tiles(const map_location& start) const
116 {
117  t_translation::terrain_code terrain = get_terrain(start);
118  std::set<map_location> result;
119  std::deque<map_location> queue;
120  result.insert(start);
121  queue.push_back(start);
122  //this is basically a breadth-first search along adjacent hexes
123  do {
125  get_adjacent_tiles(queue.front(), adj.data());
126  for (unsigned i = 0; i < adj.size(); ++i) {
127  if (on_board_with_border(adj[i]) && get_terrain(adj[i]) == terrain
128  && result.find(adj[i]) == result.end()) {
129  result.insert(adj[i]);
130  queue.push_back(adj[i]);
131  }
132  }
133  queue.pop_front();
134  } while (!queue.empty());
135  return result;
136 }
137 
139 {
140  std::set<map_location> label_locs;
141  std::string label;
142 
143 
144  for (const auto& pair : starting_positions_.left) {
145 
146  bool is_number = std::find_if(pair.first.begin(), pair.first.end(), [](char c) { return !std::isdigit(c); }) == pair.first.end();
147  if (is_number) {
148  label = VGETTEXT("Player $side_num", utils::string_map{ { "side_num", pair.first } });
149  }
150  else {
151  label = pair.first;
152  }
153 
154  disp.labels().set_label(pair.second, label);
155  label_locs.insert(pair.second);
156  }
157  return label_locs;
158 }
159 
161 {
162  return selection_.find(loc) != selection_.end();
163 }
164 
166 {
167  return on_board_with_border(loc) ? selection_.insert(loc).second : false;
168 }
169 
170 bool editor_map::set_selection(const std::set<map_location>& area)
171 {
172  clear_selection();
173  for (const map_location& loc : area) {
174  if (!add_to_selection(loc))
175  return false;
176  }
177  return true;
178 }
179 
181 {
182  return selection_.erase(loc) != 0;
183 }
184 
186 {
187  selection_.clear();
188 }
189 
191 {
192  std::set<map_location> new_selection;
193  for (int x = -1; x < w() + 1; ++x) {
194  for (int y = -1; y < h() + 1; ++y) {
195  if (selection_.find(map_location(x, y)) == selection_.end()) {
196  new_selection.emplace(x, y);
197  }
198  }
199  }
200  selection_.swap(new_selection);
201 }
202 
204 {
205  clear_selection();
207 }
208 
210 {
211  LOG_ED << selection_.size() << " " << total_width() * total_height() << "\n";
212  return static_cast<int>(selection_.size()) == total_width() * total_height();
213 }
214 
215 void editor_map::resize(int width, int height, int x_offset, int y_offset,
216  const t_translation::terrain_code & filler)
217 {
218  int old_w = w();
219  int old_h = h();
220  if (old_w == width && old_h == height && x_offset == 0 && y_offset == 0) {
221  return;
222  }
223 
224  // Determine the amount of resizing is required
225  const int left_resize = -x_offset;
226  const int right_resize = (width - old_w) + x_offset;
227  const int top_resize = -y_offset;
228  const int bottom_resize = (height - old_h) + y_offset;
229 
230  if(right_resize > 0) {
231  expand_right(right_resize, filler);
232  } else if(right_resize < 0) {
233  shrink_right(-right_resize);
234  }
235  if(bottom_resize > 0) {
236  expand_bottom(bottom_resize, filler);
237  } else if(bottom_resize < 0) {
238  shrink_bottom(-bottom_resize);
239  }
240  if(left_resize > 0) {
241  expand_left(left_resize, filler);
242  } else if(left_resize < 0) {
243  shrink_left(-left_resize);
244  }
245  if(top_resize > 0) {
246  expand_top(top_resize, filler);
247  } else if(top_resize < 0) {
248  shrink_top(-top_resize);
249  }
250 
251  // fix the starting positions
252  if(x_offset || y_offset) {
253  for (auto it = starting_positions_.left.begin(); it != starting_positions_.left.end(); ++it) {
254  starting_positions_.left.modify_data(it, [=](t_translation::coordinate & loc) { loc.add(-x_offset, -y_offset); });
255  }
256  }
257 
258  villages_.clear();
259 
260  //
261  // NOTE: I'm not sure how inefficient it is to check every loc for its village-ness as
262  // opposed to operating on the villages_ vector itself and figuring out how to handle
263  // villages on the map border. Essentially, it's possible to simply remove all members
264  // from villages_ that are no longer on the map after a resize (including those that
265  // land on a border), but that doesn't account for villages that were *on* the border
266  // prior to resizing. Those should be included. As a catch-all fix, I just check every
267  // hex. It's possible that any more complex shenanigans would be even more inefficient.
268  //
269  // -- vultraz, 2018-02-25
270  //
271  for_each_loc([this](const map_location& loc) {
272  if(is_village(loc)) {
273  villages_.push_back(loc);
274  }
275  });
276 
277  sanity_check();
278 }
279 
280 gamemap editor_map::mask_to(const gamemap& target) const
281 {
282  if (target.w() != w() || target.h() != h()) {
283  throw editor_action_exception(_("The size of the target map is different from the current map"));
284  }
285  gamemap mask(target);
286  map_location iter;
287  for (iter.x = -border_size(); iter.x < w() + border_size(); ++iter.x) {
288  for (iter.y = -border_size(); iter.y < h() + border_size(); ++iter.y) {
289  if (target.get_terrain(iter) == get_terrain(iter)) {
291  }
292  }
293  }
294  return mask;
295 }
296 
297 bool editor_map::same_size_as(const gamemap& other) const
298 {
299  return h() == other.h()
300  && w() == other.w();
301 }
302 
304 {
305  t_translation::ter_map tiles_new(tiles_.w + count, tiles_.h);
306  w_ += count;
307  for (int x = 0, x_end = tiles_.w; x != x_end; ++x) {
308  for (int y = 0, y_end = tiles_.h; y != y_end; ++y) {
309  tiles_new.get(x, y) = tiles_.get(x, y);
310  }
311  }
312  for (int x = tiles_.w, x_end = tiles_.w + count; x != x_end; ++x) {
313  for (int y = 0, y_end = tiles_.h; y != y_end; ++y) {
314  tiles_new.get(x, y) = filler == t_translation::NONE_TERRAIN ? tiles_.get(tiles_.w - 1, y) : filler;
315  }
316  }
317  tiles_ = std::move(tiles_new);
318 }
319 
321 {
322  t_translation::ter_map tiles_new(tiles_.w + count, tiles_.h);
323  w_ += count;
324  for (int x = 0, x_end = tiles_.w; x != x_end; ++x) {
325  for (int y = 0, y_end = tiles_.h; y != y_end; ++y) {
326  tiles_new.get(x + count, y) = tiles_.get(x, y);
327  }
328  }
329  for (int x = 0, x_end = count; x != x_end; ++x) {
330  for (int y = 0, y_end = tiles_.h; y != y_end; ++y) {
331  tiles_new.get(x, y) = filler == t_translation::NONE_TERRAIN ? tiles_.get(0, y) : filler;
332  }
333  }
334  tiles_ = std::move(tiles_new);
335 }
336 
338 {
339  t_translation::ter_map tiles_new(tiles_.w, tiles_.h + count);
340  h_ += count;
341  for (int x = 0, x_end = tiles_.w; x != x_end; ++x) {
342  for (int y = 0, y_end = tiles_.h; y != y_end; ++y) {
343  tiles_new.get(x, y + count) = tiles_.get(x, y);
344  }
345  }
346  for (int x = 0, x_end = tiles_.w; x != x_end; ++x) {
347  for (int y = 0, y_end = count; y != y_end; ++y) {
348  tiles_new.get(x, y) = filler == t_translation::NONE_TERRAIN ? tiles_.get(x, 0) : filler;
349  }
350  }
351  tiles_ = std::move(tiles_new);
352 }
353 
355 {
356  t_translation::ter_map tiles_new(tiles_.w, tiles_.h + count);
357  h_ += count;
358  for (int x = 0, x_end = tiles_.w; x != x_end; ++x) {
359  for (int y = 0, y_end = tiles_.h; y != y_end; ++y) {
360  tiles_new.get(x, y) = tiles_.get(x, y);
361  }
362  }
363  for (int x = 0, x_end = tiles_.w; x != x_end; ++x) {
364  for (int y = tiles_.h, y_end = tiles_.h + count; y != y_end; ++y) {
365  tiles_new.get(x, y) = filler == t_translation::NONE_TERRAIN ? tiles_.get(x, tiles_.h - 1) : filler;
366  }
367  }
368  tiles_ = std::move(tiles_new);
369 }
370 
372 {
373  if(count < 0 || count > tiles_.w) {
375  }
376  t_translation::ter_map tiles_new(tiles_.w - count, tiles_.h);
377  for (int x = 0, x_end = tiles_new.w; x != x_end; ++x) {
378  for (int y = 0, y_end = tiles_new.h; y != y_end; ++y) {
379  tiles_new.get(x, y) = tiles_.get(x, y);
380  }
381  }
382  w_ -= count;
383  tiles_ = std::move(tiles_new);
384 }
385 
386 void editor_map::shrink_left(int count)
387 {
388  if (count < 0 || count > tiles_.w) {
390  }
391  t_translation::ter_map tiles_new(tiles_.w - count, tiles_.h);
392  for (int x = 0, x_end = tiles_new.w; x != x_end; ++x) {
393  for (int y = 0, y_end = tiles_new.h; y != y_end; ++y) {
394  tiles_new.get(x, y) = tiles_.get(x + count, y);
395  }
396  }
397  w_ -= count;
398  tiles_ = std::move(tiles_new);
399 }
400 
401 void editor_map::shrink_top(int count)
402 {
403  if (count < 0 || count > tiles_.h) {
405  }
406  t_translation::ter_map tiles_new(tiles_.w, tiles_.h - count);
407  for (int x = 0, x_end = tiles_new.w; x != x_end; ++x) {
408  for (int y = 0, y_end = tiles_new.h; y != y_end; ++y) {
409  tiles_new.get(x, y) = tiles_.get(x, y + count);
410  }
411  }
412  h_ -= count;
413  tiles_ = std::move(tiles_new);
414 }
415 
417 {
418  if (count < 0 || count > tiles_.h) {
420  }
421  t_translation::ter_map tiles_new(tiles_.w, tiles_.h - count);
422  for (int x = 0, x_end = tiles_new.w; x != x_end; ++x) {
423  for (int y = 0, y_end = tiles_new.h; y != y_end; ++y) {
424  tiles_new.get(x, y) = tiles_.get(x, y);
425  }
426  }
427  h_ -= count;
428  tiles_ = std::move(tiles_new);
429 }
430 
431 
432 
433 } //end namespace editor
void expand_bottom(int count, const t_translation::terrain_code &filler)
Definition: editor_map.cpp:354
void for_each_loc(const F &f) const
Definition: map.hpp:190
int h() const
Effective map height.
Definition: map.hpp:93
const terrain_code NONE_TERRAIN
Definition: translation.hpp:58
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:517
std::vector< map_location > villages_
Definition: map.hpp:226
std::set< map_location > selection_
The selected hexes.
Definition: editor_map.hpp:204
std::set< map_location > set_starting_position_labels(display &disp)
Set labels for staring positions in the given display object.
Definition: editor_map.cpp:138
void shrink_top(int count)
Definition: editor_map.cpp:401
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:160
int border_size() const
Size of the map border.
Definition: map.hpp:96
#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:297
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:86
STL namespace.
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:215
~editor_map()
editor_map destructor
Definition: editor_map.cpp:82
t_translation::terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition: map.cpp:299
void set_terrain(const map_location &loc, const t_translation::terrain_code &terrain, const terrain_type_data::merge_mode mode=terrain_type_data::BOTH, bool replace_if_failed=false)
Clobbers over the terrain at location &#39;loc&#39;, with the given terrain.
Definition: map.cpp:379
int total_width() const
Real width of the map, including borders.
Definition: map.hpp:99
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:30
#define WRN_ED
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:89
void add(int x_diff, int y_diff)
Definition: location.hpp:163
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:320
bool on_board_with_border(const map_location &loc) const
Definition: map.cpp:372
void invert_selection()
Invert the selection, i.e.
Definition: editor_map.cpp:190
Encapsulates the map of the game.
Definition: map.hpp:34
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:115
map_display and display: classes which take care of displaying the map and game-data on the screen...
int w_
Sizes of the map area.
Definition: map.hpp:229
Manage the empty-palette in the editor.
Definition: action.cpp:29
std::array< map_location, 6 > adjacent_loc_array_t
Definition: location.hpp:170
void shrink_left(int count)
Definition: editor_map.cpp:386
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:280
bool add_to_selection(const map_location &loc)
Add a location to the selection.
Definition: editor_map.cpp:165
Encapsulates the map of the game.
Definition: location.hpp:42
Helper class, don&#39;t construct this directly.
static editor_map from_string(const config &terrain_cfg, 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:55
int w() const
Effective map width.
Definition: map.hpp:90
t_translation::ter_map tiles_
Definition: map.hpp:210
This class adds extra editor-specific functionality to a normal gamemap.
Definition: editor_map.hpp:69
std::size_t i
Definition: function.cpp:933
bool everything_selected() const
Definition: editor_map.cpp:209
EXIT_STATUS start(const config &game_conf, 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
editor_map(const config &terrain_cfg)
Empty map constructor.
Definition: editor_map.cpp:42
void expand_right(int count, const t_translation::terrain_code &filler)
Definition: editor_map.cpp:303
void clear_selection()
Clear the selection.
Definition: editor_map.cpp:185
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
int total_height() const
Real height of the map, including borders.
Definition: map.hpp:102
terrain_code & get(int x, int y)
Definition: translation.hpp:89
starting_positions starting_positions_
Definition: map.hpp:212
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:203
bool is_village(const map_location &loc) const
Definition: map.cpp:64
#define ERR_ED
void expand_top(int count, const t_translation::terrain_code &filler)
Definition: editor_map.cpp:337
std::string message
Definition: exceptions.hpp:31
map_labels & labels()
Definition: display.cpp:1402
#define e
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
mock_char c
int h_
Definition: map.hpp:230
void shrink_right(int count)
Definition: editor_map.cpp:371
bool set_selection(const std::set< map_location > &area)
Select the given area.
Definition: editor_map.cpp:170
bool remove_from_selection(const map_location &loc)
Remove a location to the selection.
Definition: editor_map.cpp:180
void shrink_bottom(int count)
Definition: editor_map.cpp:416