-
-
Notifications
You must be signed in to change notification settings - Fork 988
/
location.hpp
219 lines (173 loc) · 7.39 KB
/
location.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
/*
Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
Part of the Battle for Wesnoth Project https://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/** @file */
#pragma once
class config;
class variable_set;
#include <array>
#include <cmath>
#include <cstdlib>
#include <set>
#include <string>
#include <tuple>
#include <vector>
#include <algorithm>
#include <utility>
struct wml_loc {};
/**
* Encapsulates the map of the game.
*
* Although the game is hexagonal, the map is stored as a grid.
* Each type of terrain is represented by a multiletter terrain code.
* @todo Update for new map-format.
*/
/** Represents a location on the map. */
struct map_location {
/** Valid directions which can be moved in our hexagonal world. */
enum DIRECTION { NORTH=0, NORTH_EAST=1, SOUTH_EAST=2, SOUTH=3,
SOUTH_WEST=4, NORTH_WEST=5, NDIRECTIONS=6 };
static const std::vector<DIRECTION> & default_dirs();
static DIRECTION rotate_right(DIRECTION d, unsigned int k = 1u)
{
return (d == map_location::NDIRECTIONS) ? map_location::NDIRECTIONS : static_cast<map_location::DIRECTION>((d + (k%6u)) % 6u);
}
static DIRECTION rotate_right(DIRECTION d, signed int k)
{
return (k>=0) ? rotate_right(d, static_cast<unsigned int> (k)) : rotate_right(d, (static_cast<unsigned int>(-k) % 6u) * 5u);
}
static DIRECTION get_opposite_dir(DIRECTION d)
{
return rotate_right(d,3u);
}
static DIRECTION parse_direction(const std::string& str);
/**
* Parse_directions takes a comma-separated list, and filters out any
* invalid directions
*/
static std::vector<DIRECTION> parse_directions(const std::string& str);
static std::string write_direction(DIRECTION dir);
static std::string write_translated_direction(DIRECTION dir);
map_location() : x(-1000), y(-1000) {}
map_location(int x, int y) : x(x), y(y) {}
map_location(int x, int y, wml_loc) : x(x - 1), y(y - 1) {}
map_location(const config& cfg, const variable_set *variables = nullptr);
static const map_location & ZERO()
{
static const map_location z(0,0);
return z;
}
static const map_location & null_location()
{
static const map_location l;
return l;
}
void write(config& cfg) const;
bool valid() const { return x >= 0 && y >= 0; }
bool valid(const int parWidth, const int parHeight) const
{ return ((x >= 0) && (y >= 0) && (x < parWidth) && (y < parHeight)); }
bool valid(const int parWidth, const int parHeight, const int border) const
{ return ((x + border >= 0) && (y + border >= 0) && (x < parWidth + border) && (y < parHeight + border)); }
bool matches_range(const std::string& xloc, const std::string& yloc) const;
// Inlining those for performance reasons
bool operator<(const map_location& a) const { return std::tie(x, y) < std::tie(a.x, a.y); }
bool operator==(const map_location& a) const { return x == a.x && y == a.y; }
bool operator!=(const map_location& a) const { return !operator==(a); }
/** three-way comparator */
int do_compare(const map_location& a) const {return x == a.x ? y - a.y : x - a.x; }
// Location arithmetic operations treating the locations as vectors in
// a hex-based space. These operations form an abelian group, i.e.
// everything works as you would expect addition and subtraction to
// work, with associativity and commutativity.
map_location vector_negation() const
{
return map_location(-x, -y - (x & 1)); //subtract one if we're on an odd x coordinate
}
map_location vector_sum(const map_location &a) const
{
return map_location(*this).vector_sum_assign(a);
}
map_location& vector_sum_assign(const map_location &a)
{
y += ((x & 1) && (a.x & 1)); //add one if both x coords are odd
x += a.x;
y += a.y;
return *this;
}
map_location& vector_difference_assign(const map_location &a)
{
return vector_sum_assign(a.vector_negation());
}
// Do n step in the direction d
map_location get_direction(DIRECTION dir, unsigned int n = 1u) const;
map_location get_direction(DIRECTION dir, signed int n) const
{
return (n >= 0) ? get_direction(dir, static_cast<unsigned int> (n)) : get_direction(get_opposite_dir(dir), static_cast<unsigned int> (-n));
}
enum RELATIVE_DIR_MODE { DEFAULT , RADIAL_SYMMETRY };
DIRECTION get_relative_dir(const map_location & loc, map_location::RELATIVE_DIR_MODE mode /*= map_location::RADIAL_SYMMETRY*/ ) const;
DIRECTION get_relative_dir(const map_location & loc) const; //moved the default setting to .cpp file for ease of testing
// Express as a vector in the basis N, NE. N, and NE may be obtained by zero.get_direction(NORTH), ...(NORTH_EAST), respectively.
std::pair<int,int> get_in_basis_N_NE() const;
// Rotates the map_location clockwise in 60 degree increments around a center point. Negative numbers of steps are permitted.
map_location rotate_right_around_center(const map_location & center, int k) const;
friend std::size_t hash_value(const map_location& a);
int wml_x() const { return x + 1; }
int wml_y() const { return y + 1; }
void set_wml_x(int v) { x = v - 1; }
void set_wml_y(int v) { y = v - 1; }
//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().
void add(int x_diff, int y_diff) { x += x_diff; y += y_diff; }
map_location plus(int x_diff, int y_diff) const { return map_location(x + x_diff, y + y_diff); }
int x, y;
};
using adjacent_loc_array_t = std::array<map_location, 6>;
/** Function which tells if two locations are adjacent. */
bool tiles_adjacent(const map_location& a, const map_location& b);
/**
* Function which, given a location, will place all adjacent locations in res.
* res must point to an array of 6 location objects.
*/
void get_adjacent_tiles(const map_location& a, map_location* res);
/**
* Function which gives the number of hexes between two tiles
* (i.e. the minimum number of hexes that have to be traversed
* to get from one hex to the other).
*/
std::size_t distance_between(const map_location& a, const map_location& b);
/**
* Write a set of locations into a config using ranges,
* adding keys x=x1,..,xn and y=y1a-y1b,..,yna-ynb
*/
void write_location_range(const std::set<map_location>& locs, config& cfg);
/**
* Parse x,y keys of a config into a vector of locations
*
* Throws bad_lexical_cast if it fails to parse.
*/
void read_locations(const config& cfg, std::vector<map_location>& locs);
/** Write a vector of locations into a config
* adding keys x=x1,x2,..,xn and y=y1,y2,..,yn */
void write_locations(const std::vector<map_location>& locs, config& cfg);
/** Dumps a position on a stream, for debug purposes. */
std::ostream &operator<<(std::ostream &s, const map_location& l);
/** Dumps a vector of positions on a stream, for debug purposes. */
std::ostream &operator<<(std::ostream &s, const std::vector<map_location>& v);
namespace std {
template<>
struct hash<map_location> {
std::size_t operator()(const map_location& l) const noexcept {
// The 2000 bias supposedly ensures that the correct x is recovered for negative y
// This implementation copied from the Lua location_set
return (l.wml_x()) * 16384 + (l.wml_y()) + 2000;
}
};
}