The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
location.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2016 by David White <[email protected]>
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 
15 /**
16  * @file
17  * Routines related to game-maps, terrain, locations, directions. etc.
18  */
19 
20 #include "global.hpp"
21 
22 #include <cassert>
23 
24 #include "map/location.hpp"
25 
26 #include "config.hpp"
27 #include "formula/string_utils.hpp"
28 #include "gettext.hpp"
29 #include "util.hpp"
30 
31 #include <boost/functional/hash.hpp>
32 
33 #define ERR_CF LOG_STREAM(err, config)
34 #define LOG_G LOG_STREAM(info, general)
35 #define DBG_G LOG_STREAM(debug, general)
36 
37 std::ostream &operator<<(std::ostream &s, map_location const &l) {
38  s << (l.x + 1) << ',' << (l.y + 1);
39  return s;
40 }
41 std::ostream &operator<<(std::ostream &s, std::vector<map_location> const &v) {
42  std::vector<map_location>::const_iterator i = v.begin();
43  for(; i!= v.end(); ++i) {
44  s << "(" << *i << ") ";
45  }
46  return s;
47 }
48 
49 /**
50  * Default list of directions
51  *
52  * Moved out of inline, because boost assign list_of is somewhat expensive...
53  *
54  **/
55 const std::vector<map_location::DIRECTION> & map_location::default_dirs() {
56  static const std::vector<map_location::DIRECTION> dirs = {map_location::NORTH,
59  return dirs;
60 }
61 
62 /** Moved out of inline because of the boost dependency **/
63 std::size_t hash_value(map_location const & a){
64  boost::hash<size_t> h;
65  return h( (a.x << 16) ^ a.y );
66 }
67 
68 
70 {
71  if(str.empty()) {
72  return NDIRECTIONS;
73  }
74 
75  // Syntax: [-] (n|ne|se|s|sw|nw) [:cw|:ccw]
76  // - means "take opposite direction" and has higher precedence
77  // :cw and :ccw mean "one step (counter-)clockwise"
78  // Parentheses can be used for grouping or to apply an operator more than once
79 
80  const size_t open = str.find_first_of('('), close = str.find_last_of(')');
81  if (open != std::string::npos && close != std::string::npos) {
82  std::string sub = str.substr(open + 1, close - open - 1);
84  sub = str;
85  sub.replace(open, close - open + 1, write_direction(dir));
86  return parse_direction(sub);
87  }
88 
89  const size_t start = str[0] == '-' ? 1 : 0;
90  const size_t end = str.find_first_of(':');
91  const std::string& main_dir = str.substr(start, end - start);
93 
94  if (main_dir == "n") {
95  dir = NORTH;
96  } else if (main_dir == "ne") {
97  dir = NORTH_EAST;
98  } else if (main_dir == "se") {
99  dir = SOUTH_EAST;
100  } else if (main_dir == "s") {
101  dir = SOUTH;
102  } else if (main_dir == "sw") {
103  dir = SOUTH_WEST;
104  } else if (main_dir == "nw") {
105  dir = NORTH_WEST;
106  } else {
107  return NDIRECTIONS;
108  }
109 
110  if (start == 1) {
111  dir = get_opposite_dir(dir);
112  }
113 
114  if (end != std::string::npos) {
115  const std::string rel_dir = str.substr(end + 1);
116  if (rel_dir == "cw") {
117  dir = rotate_right(dir, 1);
118  } else if (rel_dir == "ccw") {
119  dir = rotate_right(dir, -1);
120  } else {
121  return NDIRECTIONS;
122  }
123  }
124 
125  return dir;
126 }
127 
128 std::vector<map_location::DIRECTION> map_location::parse_directions(const std::string& str)
129 {
131  std::vector<map_location::DIRECTION> to_return;
132  std::vector<std::string> dir_strs = utils::split(str);
133  std::vector<std::string>::const_iterator i, i_end=dir_strs.end();
134  for(i = dir_strs.begin(); i != i_end; ++i) {
136  // Filter out any invalid directions
137  if(temp != NDIRECTIONS) {
138  to_return.push_back(temp);
139  }
140  }
141  return to_return;
142 }
143 
145 {
146  switch(dir) {
147  case NORTH:
148  return std::string("n");
149  case NORTH_EAST:
150  return std::string("ne");
151  case NORTH_WEST:
152  return std::string("nw");
153  case SOUTH:
154  return std::string("s");
155  case SOUTH_EAST:
156  return std::string("se");
157  case SOUTH_WEST:
158  return std::string("sw");
159  default:
160  return std::string();
161 
162  }
163 }
164 
166 {
167  switch(dir) {
168  case NORTH:
169  return _("North");
170  case NORTH_EAST:
171  return _("North East");
172  case NORTH_WEST:
173  return _("North West");
174  case SOUTH:
175  return _("South");
176  case SOUTH_EAST:
177  return _("South East");
178  case SOUTH_WEST:
179  return _("South West");
180  default:
181  return std::string();
182 
183  }
184 }
185 
186 map_location::map_location(const config& cfg, const variable_set *variables) :
187  x(-1000),
188  y(-1000)
189 {
190  std::string xs = cfg["x"], ys = cfg["y"];
191  if (variables)
192  {
193  xs = utils::interpolate_variables_into_string( xs, *variables);
194  ys = utils::interpolate_variables_into_string( ys, *variables);
195  }
196  // The co-ordinates in config files will be 1-based,
197  // while we want them as 0-based.
198  if(xs.empty() == false && xs != "recall")
199  x = std::stoi(xs) - 1;
200 
201  if(ys.empty() == false && ys != "recall")
202  y = std::stoi(ys) - 1;
203 }
204 
205 void map_location::write(config& cfg) const
206 {
207  cfg["x"] = x + 1;
208  cfg["y"] = y + 1;
209 }
210 
211 static bool is_vertically_higher_than ( const map_location & m1, const map_location & m2 ) {
212  return (is_even(m1.x) && is_odd(m2.x)) ? (m1.y <= m2.y) : (m1.y < m2.y);
213 }
214 
216 {
218 }
219 
221 {
222  if (opt == map_location::DEFAULT) {
224 
225  int dx = loc.x - x;
226  int dy = loc.y - y;
227  if (loc.x%2==0 && x%2==1) dy--;
228 
229  if (dx==0 && dy==0) return NDIRECTIONS;
230 
231  int dist = abs(dx); // Distance from north-south line
232  int dist_diag_SW_NE = abs(dy + (dx + (dy>0?0:1) )/2); // Distance from diagonal line SW-NE
233  int dist_diag_SE_NW = abs(dy - (dx - (dy>0?0:1) )/2); // Distance from diagonal line SE-NW
234 
235  if (dy > 0) dir = SOUTH;
236  else dir = NORTH;
237 
238  if (dist_diag_SE_NW < dist) {
239  if (dx>0) dir = SOUTH_EAST;
240  else dir = NORTH_WEST;
241  dist = dist_diag_SE_NW;
242  }
243  if (dist_diag_SW_NE < dist) {
244  if (dx>0) dir = NORTH_EAST;
245  else dir = SOUTH_WEST;
246  }
247  return dir;
248  } else {
249  map_location temp(loc);
250 
251  if (is_vertically_higher_than(temp,*this)) {
252  temp = temp.rotate_right_around_center(*this,1u);
253  if (!is_vertically_higher_than(temp,*this)) {
255  }
256  temp = temp.rotate_right_around_center(*this,1u);
257  if (!is_vertically_higher_than(temp,*this)) {
258  return map_location::NORTH;
259  }
261  } else if (is_vertically_higher_than(*this,temp)) {
262  temp = temp.rotate_right_around_center(*this,1u);
263  if (!is_vertically_higher_than(*this,temp)) {
265  }
266  temp = temp.rotate_right_around_center(*this,1u);
267  if (!is_vertically_higher_than(*this,temp)) {
268  return map_location::SOUTH;
269  }
271  } else if (temp.x > x) {
273  } else if (temp.x < x) {
275  } else {
277  }
278  }
279 }
280 
281 std::pair<int,int> map_location::get_in_basis_N_NE() const {
282  map_location temp(*this);
283  std::pair<int, int> ret;
284 
285  ret.second = temp.x;
286  temp = temp.get_direction(SOUTH_WEST,temp.x);
287  assert(temp.x == 0);
288 
289  ret.first = -temp.y;
290  temp = temp.get_direction(NORTH,temp.y);
291  assert(temp.y == 0);
292 
293  temp = temp.get_direction(NORTH, ret.first);
294  temp = temp.get_direction(NORTH_EAST, ret.second);
295  assert(temp == *this);
296 
297  return ret;
298 }
299 
301  map_location temp(*this);
302  temp.vector_difference_assign(center);
303 
304  std::pair<int,int> coords = temp.get_in_basis_N_NE();
307 
308  return center.get_direction(d1, coords.first).get_direction(d2, coords.second);
309 }
310 
311 bool map_location::matches_range(const std::string& xloc, const std::string &yloc) const
312 {
313  if(std::find(xloc.begin(),xloc.end(),',') != xloc.end()
314  || std::find(yloc.begin(),yloc.end(),',') != yloc.end()) {
315  std::vector<std::string> xlocs = utils::split(xloc);
316  std::vector<std::string> ylocs = utils::split(yloc);
317 
318  size_t size;
319  for(size = xlocs.size(); size < ylocs.size(); ++size) {
320  xlocs.push_back("");
321  }
322  while(size > ylocs.size()) {
323  ylocs.push_back("");
324  }
325  for(size_t i = 0; i != size; ++i) {
326  if(matches_range(xlocs[i],ylocs[i]))
327  return true;
328  }
329  return false;
330  }
331  if(!xloc.empty()) {
332  const std::string::const_iterator dash =
333  std::find(xloc.begin(),xloc.end(),'-');
334  if(dash != xloc.begin() && dash != xloc.end()) {
335  const std::string beg(xloc.begin(),dash);
336  const std::string end(dash+1,xloc.end());
337 
338  const int bot = std::stoi(beg) - 1;
339  const int top = std::stoi(end) - 1;
340 
341  if(x < bot || x > top)
342  return false;
343  } else {
344  const int xval = std::stoi(xloc) - 1;
345  if(xval != x)
346  return false;
347  }
348  }
349  if(!yloc.empty()) {
350  const std::string::const_iterator dash =
351  std::find(yloc.begin(),yloc.end(),'-');
352 
353  if(dash != yloc.begin() && dash != yloc.end()) {
354  const std::string beg(yloc.begin(),dash);
355  const std::string end(dash+1,yloc.end());
356 
357  const int bot = std::stoi(beg) - 1;
358  const int top = std::stoi(end) - 1;
359 
360  if(y < bot || y > top)
361  return false;
362  } else {
363  const int yval = std::stoi(yloc) - 1;
364  if(yval != y)
365  return false;
366  }
367  }
368  return true;
369 }
370 
371 void write_location_range(const std::set<map_location>& locs, config& cfg)
372 {
373  if(locs.empty()){
374  cfg["x"] = "";
375  cfg["y"] = "";
376  return;
377  }
378 
379  // need that operator< uses x first
380  assert(map_location(0,1) < map_location(1,0));
381 
382  std::stringstream x, y;
383  std::set<map_location>::const_iterator
384  i = locs.begin(),
385  first = i,
386  last = i;
387  x << (i->x + 1);
388  y << (i->y + 1);
389 
390  for(++i; i != locs.end(); ++i) {
391  if(i->x != first->x || i->y != last->y+1){
392  if(last->y != first->y)
393  y << "-" << (last->y + 1);
394  x << "," << (i->x + 1);
395  y << "," << (i->y + 1);
396  first = i;
397  }
398  last = i;
399  }
400  // finish last range
401  if(last->y != first->y)
402  y << "-" << (last->y + 1);
403 
404  cfg["x"] = x.str();
405  cfg["y"] = y.str();
406 }
407 
409 {
410  return map_location(lexical_cast<int>(xi)-1, lexical_cast<int>(yi)-1);
411 }
412 
413 void read_locations(const config& cfg, std::vector<map_location>& locs)
414 {
415  const std::vector<std::string> xvals = utils::split(cfg["x"]);
416  const std::vector<std::string> yvals = utils::split(cfg["y"]);
417 
418  if (xvals.size() != yvals.size()) {
419  throw bad_lexical_cast();
420  }
421 
422  std::transform(xvals.begin(), xvals.end(), yvals.begin(), std::back_inserter(locs), &read_locations_helper);
423 }
424 
425 void write_locations(const std::vector<map_location>& locs, config& cfg)
426 {
427  std::stringstream x, y;
428 
429  std::vector<map_location>::const_iterator i = locs.begin(),
430  end = locs.end();
431 
432  for(; i != end; ++i) {
433  x << (i->x + 1);
434  y << (i->y + 1);
435  if(i+1 != end){
436  x << ",";
437  y << ",";
438  }
439  }
440 
441  cfg["x"] = x.str();
442  cfg["y"] = y.str();
443 }
void read_locations(const config &cfg, std::vector< map_location > &locs)
Parse x,y keys of a config into a vector of locations.
Definition: location.cpp:413
static DIRECTION parse_direction(const std::string &str)
Definition: location.cpp:69
static std::string write_translated_direction(DIRECTION dir)
Definition: location.cpp:165
std::string interpolate_variables_into_string(const std::string &str, const string_map *const symbols)
Function which will interpolate variables, starting with '$' in the string 'str' with the equivalent ...
bool matches_range(const std::string &xloc, const std::string &yloc) const
Definition: location.cpp:311
static bool is_vertically_higher_than(const map_location &m1, const map_location &m2)
Definition: location.cpp:211
map_location rotate_right_around_center(const map_location &center, int k) const
Definition: location.cpp:300
std::ostream & operator<<(std::ostream &s, map_location const &l)
Dumps a position on a stream, for debug purposes.
Definition: location.cpp:37
bool is_odd(T num)
Definition: util.hpp:37
map_location & vector_difference_assign(const map_location &a)
Definition: location.hpp:219
#define h
DIRECTION get_relative_dir(const map_location &loc, map_location::RELATIVE_DIR_MODE mode) const
Definition: location.cpp:220
GLint GLint GLint GLint GLint GLint y
Definition: glew.h:1220
static std::vector< DIRECTION > parse_directions(const std::string &str)
Parse_directions takes a comma-separated list, and filters out any invalid directions.
Definition: location.cpp:128
Definitions for the interface to Wesnoth Markup Language (WML).
GLdouble l
Definition: glew.h:6966
GLuint GLenum GLenum transform
Definition: glew.h:11418
std::size_t hash_value(map_location const &a)
Moved out of inline because of the boost dependency.
Definition: location.cpp:63
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:82
GLuint GLuint end
Definition: glew.h:1221
static std::string sub(const std::string &s)
Private function to surround an argument with brackets.
const GLdouble * v
Definition: glew.h:1359
GLuint start
Definition: glew.h:1221
GLboolean GLboolean GLboolean GLboolean a
Definition: glew.h:7319
static DIRECTION get_opposite_dir(DIRECTION d)
Definition: location.hpp:163
static const std::vector< DIRECTION > & default_dirs()
Default list of directions.
Definition: location.cpp:55
Templates and utility-routines for strings and numbers.
void write(config &cfg) const
Definition: location.cpp:205
Encapsulates the map of the game.
Definition: location.hpp:38
void write_location_range(const std::set< map_location > &locs, config &cfg)
Write a set of locations into a config using ranges, adding keys x=x1,..,xn and y=y1a-y1b,..,yna-ynb.
Definition: location.cpp:371
static DIRECTION rotate_right(DIRECTION d, unsigned int k=1u)
Inlined bodies.
Definition: location.hpp:155
size_t i
Definition: function.cpp:1057
GLenum GLuint coords
Definition: glew.h:5805
GLint GLint GLint GLint GLint x
Definition: glew.h:1220
DIRECTION
Valid directions which can be moved in our hexagonal world.
Definition: location.hpp:40
GLsizeiptr size
Definition: glew.h:1649
bool find(E event, F functor)
Tests whether an event handler is available.
std::pair< int, int > get_in_basis_N_NE() const
Definition: location.cpp:281
GLint * first
Definition: glew.h:1496
map_location get_direction(DIRECTION d, unsigned int n=1u) const
Definition: location.hpp:232
std::vector< std::string > split(std::string const &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
GLdouble s
Definition: glew.h:1358
Thrown when a lexical_cast fails.
static std::string write_direction(DIRECTION dir)
Definition: location.cpp:144
GLsizei const GLcharARB ** string
Definition: glew.h:4503
static map_location read_locations_helper(const std::string &xi, const std::string &yi)
Definition: location.cpp:408
bool is_even(T num)
Definition: util.hpp:34
void write_locations(const std::vector< map_location > &locs, config &cfg)
Write a vector of locations into a config adding keys x=x1,x2,..,xn and y=y1,y2,..,yn.
Definition: location.cpp:425