The Battle for Wesnoth  1.19.12+dev
location.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2025
3  by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #pragma once
17 
18 class config;
20 class variable_set;
21 
22 #include <array>
23 #include <set>
24 #include <string>
25 #include <tuple>
26 #include <vector>
27 #include <utility>
28 
29 struct wml_loc {};
30 
31 /// Represents a map location in cubic hexagonal coordinates.
32 /// See <https://www.redblobgames.com/grids/hexagons/#coordinates-cube> for a more detailed explanation.
34  int q, r, s;
35 };
36 
37 /**
38  * Encapsulates the map of the game.
39  *
40  * Although the game is hexagonal, the map is stored as a grid.
41  * Each type of terrain is represented by a multiletter terrain code.
42  * @todo Update for new map-format.
43  */
44 /** Represents a location on the map. */
45 struct map_location {
46  /** Valid directions which can be moved in our hexagonal world. */
47  enum class direction {
48  north,
49  north_east,
50  south_east,
51  south,
52  south_west,
53  north_west,
55  };
56 
57  static std::vector<direction> all_directions();
58 
59  /** Returns the direction one would face having taken @a steps clockwise around an undefined center. */
60  static constexpr direction rotate_direction(direction d, int steps = 1)
61  {
64  }
65 
66  // Compensate for weirdness with the C++ % operator.
67  // The plain formula (d + (steps % 6)) % 6 works for almost every case, but it returns incorrect
68  // results for a counterclockwise rotation on direction::north. Instead, for any negative steps,
69  // cancel out the sign and scale by the highest possible direction value (viz., 5). This has the
70  // effect of a clockwise rotation ending at the same value as the specified counter-clockwise op.
71  int adjusted_steps = steps >= 0 ? steps : steps * -5;
72  return direction{(static_cast<int>(d) + (adjusted_steps % 6)) % 6};
73  }
74 
76  {
77  return rotate_direction(d, 3);
78  }
79 
80  static direction parse_direction(const std::string& str);
81 
82  /**
83  * Parse_directions takes a comma-separated list, and filters out any
84  * invalid directions
85  */
86  static std::vector<direction> parse_directions(const std::string& str);
87  static std::string write_direction(direction dir);
88  static std::string write_translated_direction(direction dir);
89 
90  map_location() : x(-1000), y(-1000) {}
91  map_location(int x, int y) : x(x), y(y) {}
92  map_location(int x, int y, wml_loc) : x(x - 1), y(y - 1) {}
94  map_location(const config& cfg, const variable_set *variables = nullptr);
95 
96  static const map_location& ZERO()
97  {
98  static const map_location z(0,0);
99  return z;
100  }
101 
102  static const map_location& null_location()
103  {
104  static const map_location l;
105  return l;
106  }
107 
108  void write(config& cfg) const;
109 
110  bool valid() const { return x >= 0 && y >= 0; }
111 
112  bool valid(const int parWidth, const int parHeight) const
113  { return ((x >= 0) && (y >= 0) && (x < parWidth) && (y < parHeight)); }
114 
115  bool valid(const int parWidth, const int parHeight, const int border) const
116  { return ((x + border >= 0) && (y + border >= 0) && (x < parWidth + border) && (y < parHeight + border)); }
117 
118  bool matches_range(const std::string& xloc, const std::string& yloc) const;
119 
120  // Inlining those for performance reasons
121  bool operator<(const map_location& a) const { return std::tie(x, y) < std::tie(a.x, a.y); }
122  bool operator==(const map_location& a) const { return x == a.x && y == a.y; }
123  bool operator!=(const map_location& a) const { return !operator==(a); }
124 
125  /** three-way comparator */
126  int do_compare(const map_location& a) const {return x == a.x ? y - a.y : x - a.x; }
127 
128  // Location arithmetic operations treating the locations as vectors in
129  // a hex-based space. These operations form an abelian group, i.e.
130  // everything works as you would expect addition and subtraction to
131  // work, with associativity and commutativity.
133  {
134  return map_location(-x, -y - (x & 1)); //subtract one if we're on an odd x coordinate
135  }
136 
138  {
139  return map_location(*this).vector_sum_assign(a);
140  }
141 
143  {
144  y += ((x & 1) && (a.x & 1)); //add one if both x coords are odd
145  x += a.x;
146  y += a.y;
147  return *this;
148  }
149 
151  {
153  }
154 
155  // Do n step in the direction d
156  map_location get_direction(direction dir, unsigned int n = 1u) const;
157  map_location get_direction(direction dir, signed int n) const
158  {
159  return (n >= 0) ? get_direction(dir, static_cast<unsigned int> (n)) : get_direction(get_opposite_direction(dir), static_cast<unsigned int> (-n));
160  }
161 
163  direction get_relative_dir(const map_location& loc, map_location::RELATIVE_DIR_MODE mode /*= map_location::RADIAL_SYMMETRY*/ ) const;
164  direction get_relative_dir(const map_location& loc) const; //moved the default setting to .cpp file for ease of testing
165 
167  int q = x;
168  int r = y - int((x - abs(x) % 2) / 2);
169  int s = -q - r;
170  return cubic_location{q, r, s};
171  }
173  int x = h.q;
174  int y = h.r + int((h.q - abs(h.q) % 2) / 2);
175  return map_location(x, y);
176  }
177 
178  std::vector<map_location> get_ring(int min, int max) const;
179 
180  // Rotates the map_location clockwise in 60 degree increments around a center point. Negative numbers of steps are permitted.
181  map_location rotate_right_around_center(const map_location& center, int k) const;
182 
183  friend std::size_t hash_value(const map_location& a);
184 
185  int wml_x() const { return x + 1; }
186  int wml_y() const { return y + 1; }
187 
188  void set_wml_x(int v) { x = v - 1; }
189  void set_wml_y(int v) { y = v - 1; }
190  //on purpose these functions don't take map_location objects, if you use map_location objects to store 'differences' between 2 locations use vector_sum().
191  void add(int x_diff, int y_diff) { x += x_diff; y += y_diff; }
192  map_location plus(int x_diff, int y_diff) const { return map_location(x + x_diff, y + y_diff); }
193 
194 
195  int x, y;
196 };
197 
198 /** Function which tells if two locations are adjacent. */
199 bool tiles_adjacent(const map_location& a, const map_location& b);
200 
201 /**
202  * Function which, given a location, will place all adjacent locations in res.
203  * res must point to an array of 6 location objects.
204  */
205 void get_adjacent_tiles(const map_location& a, map_location* res);
206 
207 /** Returns an array of the six hexes adjacent to @p center. */
208 std::array<map_location, 6> get_adjacent_tiles(const map_location& center);
209 
210 /**
211  * Function which gives the number of hexes between two tiles
212  * (i.e. the minimum number of hexes that have to be traversed
213  * to get from one hex to the other).
214  */
215 std::size_t distance_between(const map_location& a, const map_location& b);
216 
217 /**
218  * Write a set of locations into a config using ranges,
219  * adding keys x=x1,..,xn and y=y1a-y1b,..,yna-ynb
220  */
221 void write_location_range(const std::set<map_location>& locs, config& cfg);
222 
223 /**
224  * Parse x,y keys of a config into a vector of locations
225  *
226  * Throws bad_lexical_cast if it fails to parse.
227  */
228 void read_locations(const config& cfg, std::vector<map_location>& locs);
229 
230 /** Write a vector of locations into a config
231  * adding keys x=x1,x2,..,xn and y=y1,y2,..,yn */
232 void write_locations(const std::vector<map_location>& locs, config& cfg);
233 
234 /** Dumps a position on a stream, for debug purposes. */
235 std::ostream& operator<<(std::ostream& s, const map_location& l);
236 /** Dumps a vector of positions on a stream, for debug purposes. */
237 std::ostream& operator<<(std::ostream& s, const std::vector<map_location>& v);
238 
239 /** Print a direction's string representation to stream. */
240 std::ostream& operator<<(std::ostream& s, map_location::direction dir);
241 
242 namespace std {
243 template<>
244 struct hash<map_location> {
245  std::size_t operator()(const map_location& l) const noexcept {
246  // The 2000 bias supposedly ensures that the correct x is recovered for negative y
247  // This implementation copied from the Lua location_set
248  return (l.wml_x()) * 16384 + (l.wml_y()) + 2000;
249  }
250 };
251 }
map_location loc
Definition: move.cpp:172
Variant for storing WML attributes.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:158
@ border
The border of the 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:512
std::size_t distance_between(const map_location &a, const map_location &b)
Function which gives the number of hexes between two tiles (i.e.
Definition: location.cpp:583
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,.....
Definition: location.cpp:492
std::ostream & operator<<(std::ostream &s, const map_location &l)
Dumps a position on a stream, for debug purposes.
Definition: location.cpp:36
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:480
bool tiles_adjacent(const map_location &a, const map_location &b)
Function which tells if two locations are adjacent.
Definition: location.cpp:540
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,...
Definition: location.cpp:436
Represents a map location in cubic hexagonal coordinates.
Definition: location.hpp:33
Encapsulates the map of the game.
Definition: location.hpp:45
map_location & vector_sum_assign(const map_location &a)
Definition: location.hpp:142
map_location & vector_difference_assign(const map_location &a)
Definition: location.hpp:150
static map_location from_cubic(cubic_location h)
Definition: location.hpp:172
bool valid(const int parWidth, const int parHeight) const
Definition: location.hpp:112
static std::string write_direction(direction dir)
Definition: location.cpp:154
map_location vector_negation() const
Definition: location.hpp:132
map_location(int x, int y)
Definition: location.hpp:91
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:138
void add(int x_diff, int y_diff)
Definition: location.hpp:191
friend std::size_t hash_value(const map_location &a)
Definition: location.cpp:73
static std::vector< direction > all_directions()
Definition: location.cpp:61
void set_wml_y(int v)
Definition: location.hpp:189
bool operator==(const map_location &a) const
Definition: location.hpp:122
map_location vector_sum(const map_location &a) const
Definition: location.hpp:137
map_location get_direction(direction dir, unsigned int n=1u) const
Definition: location.cpp:362
map_location rotate_right_around_center(const map_location &center, int k) const
Definition: location.cpp:299
bool valid() const
Definition: location.hpp:110
int wml_y() const
Definition: location.hpp:186
std::vector< map_location > get_ring(int min, int max) const
Definition: location.cpp:401
bool operator!=(const map_location &a) const
Definition: location.hpp:123
static constexpr direction get_opposite_direction(direction d)
Definition: location.hpp:75
void set_wml_x(int v)
Definition: location.hpp:188
bool operator<(const map_location &a) const
Definition: location.hpp:121
cubic_location to_cubic() const
Definition: location.hpp:166
direction
Valid directions which can be moved in our hexagonal world.
Definition: location.hpp:47
bool valid(const int parWidth, const int parHeight, const int border) const
Definition: location.hpp:115
direction get_relative_dir(const map_location &loc, map_location::RELATIVE_DIR_MODE mode) const
Definition: location.cpp:238
static const map_location & null_location()
Definition: location.hpp:102
int wml_x() const
Definition: location.hpp:185
map_location(int x, int y, wml_loc)
Definition: location.hpp:92
static direction parse_direction(const std::string &str)
Definition: location.cpp:79
map_location get_direction(direction dir, signed int n) const
Definition: location.hpp:157
static std::string write_translated_direction(direction dir)
Definition: location.cpp:174
void write(config &cfg) const
Definition: location.cpp:223
bool matches_range(const std::string &xloc, const std::string &yloc) const
Definition: location.cpp:320
map_location plus(int x_diff, int y_diff) const
Definition: location.hpp:192
int do_compare(const map_location &a) const
three-way comparator
Definition: location.hpp:126
static constexpr direction rotate_direction(direction d, int steps=1)
Returns the direction one would face having taken steps clockwise around an undefined center.
Definition: location.hpp:60
static const map_location & ZERO()
Definition: location.hpp:96
std::size_t operator()(const map_location &l) const noexcept
Definition: location.hpp:245
static map_location::direction n
static map_location::direction s
#define d
#define h
#define b