Freeciv21
Develop your civilization from humble roots to a global empire
layer_roads.cpp
Go to the documentation of this file.
1 // SPDX-License-Identifier: GPLv3-or-later
2 // SPDX-FileCopyrightText: Freeciv authors
3 // SPDX-FileCopyrightText: Freeciv21 authors
4 // SPDX-FileCopyrightText: Louis Moureaux <m_louis30@yahoo.com>
5 
6 #include "layer_roads.h"
7 
8 #include "bitvector.h"
9 #include "extras.h"
10 #include "fc_types.h"
11 #include "layer.h"
12 #include "terrain.h"
13 #include "tilespec.h"
14 #include "unit.h"
15 
16 #include <bitset>
17 
26 namespace freeciv {
27 
32  : freeciv::layer(ts, LAYER_ROADS)
33 {
34 }
35 
40  const QString &tag, extrastyle_id style)
41 {
42  // Make sure they are all sized properly.
43  m_all_combined.resize(terrain_count());
44  m_all_separate.resize(terrain_count());
46 
47  // This would get a bit repetitive without a macro
48 #define INIT(vector, func) \
49  /* Make sure we have enough space */ \
50  terrain_type_iterate(terrain) \
51  { \
52  /* Add an entry for this extra */ \
53  auto &data = vector[terrain_index(terrain)].emplace_back(); \
54  /* Initializes it */ \
55  initialize_corners(data, extra, tag, terrain); \
56  func(data, tag, terrain); \
57  } \
58  terrain_type_iterate_end;
59 
60  if (style == ESTYLE_ROAD_ALL_COMBINED) {
62  } else if (style == ESTYLE_ROAD_ALL_SEPARATE) {
64  } else if (style == ESTYLE_ROAD_PARITY_COMBINED) {
66  }
67 #undef INIT
68 }
69 
74  const extra_type *extra,
75  const QString &tag,
76  const terrain *terrain)
77 {
78  data.extra = extra;
79 
80  // Load special corner road sprites
81  for (int i = 0; i < tileset_num_valid_dirs(tileset()); ++i) {
82  auto dir = tileset_valid_dirs(tileset())[i];
83  if (!is_cardinal_tileset_dir(tileset(), dir)) {
84  QString dtn = QStringLiteral("_c_%1").arg(dir_get_tileset_name(dir));
85  data.corners[dir] =
86  load_sprite(make_tag_terrain_list(tag, dtn, terrain), false);
87  }
88  }
89 }
90 
95  const QString &tag,
96  const terrain *terrain)
97 {
98  // Just go around clockwise, with all combinations.
99  auto count = 1 << tileset_num_valid_dirs(tileset());
100  for (int i = 0; i < count; ++i) {
101  QString idx_str =
102  QStringLiteral("_%1").arg(valid_index_str(tileset(), i));
103  data.sprites[i] =
104  load_sprite(make_tag_terrain_list(tag, idx_str, terrain), true);
105  }
106 }
107 
112  const QString &tag,
113  const terrain *terrain)
114 {
115  // Load the isolated sprite
116  data.isolated =
117  load_sprite(make_tag_terrain_list(tag, "_isolated", terrain), true);
118 
119  // Load the directional sprite options, one per direction
120  for (int i = 0; i < tileset_num_valid_dirs(tileset()); ++i) {
121  auto dir = tileset_valid_dirs(tileset())[i];
122  QString dir_name = QStringLiteral("_%1").arg(dir_get_tileset_name(dir));
123  data.sprites[i] =
124  load_sprite(make_tag_terrain_list(tag, dir_name, terrain), true);
125  }
126 }
127 
132  const QString &tag,
133  const terrain *terrain)
134 {
135  // Load the isolated sprite
136  data.isolated =
137  load_sprite(make_tag_terrain_list(tag, "_isolated", terrain), true);
138 
139  int num_index = 1 << (tileset_num_valid_dirs(tileset()) / 2);
140 
141  // Load the directional sprites
142  // The comment below exemplifies square tiles:
143  // additional sprites for each road type: 16 each for cardinal and diagonal
144  // directions. Each set of 16 provides a NSEW-indexed sprite to provide
145  // connectors for all rails in the cardinal/diagonal directions. The 0
146  // entry is unused (the "isolated" sprite is used instead). */
147  for (int i = 1; i < num_index; i++) {
148  QString cs = QStringLiteral("_c_");
149  QString ds = QStringLiteral("_d_");
150 
151  for (int j = 0; j < tileset_num_valid_dirs(tileset()) / 2; j++) {
152  int value = (i >> j) & 1;
153  cs += QStringLiteral("%1%2")
155  tileset_valid_dirs(tileset())[2 * j]))
156  .arg(value);
157  ds += QStringLiteral("%1%2")
159  tileset_valid_dirs(tileset())[2 * j + 1]))
160  .arg(value);
161  }
162 
163  data.sprites.first[i] =
164  load_sprite(make_tag_terrain_list(tag, cs, terrain), true);
165  data.sprites.second[i] =
166  load_sprite(make_tag_terrain_list(tag, ds, terrain), true);
167  }
168 }
169 
170 /* Define some helper functions to decide where to draw roads. */
171 namespace {
175 bool is_hidden(const extra_type *extra, const bv_extras extras)
176 {
177  extra_type_list_iterate(extra->hiders, hider)
178  {
179  if (BV_ISSET(extras, extra_index(hider))) {
180  return true;
181  }
182  }
184 
185  return false;
186 }
187 
191 bool connects(const struct tileset *t, const extra_type *extra,
192  direction8 dir, const bv_extras extras,
193  const bv_extras extras_near, const terrain *terrain_near)
194 {
195  // If the extra isn't on the starting tile, it doesn't connect.
196  if (!BV_ISSET(extras, extra_index(extra))) {
197  return false;
198  }
199 
200  // Some extras (e.g. rivers) only connect in cardinal directions.
201  if (is_cardinal_only_road(extra)
202  && is_cardinal_tileset_dir(t, static_cast<direction8>(dir))) {
203  return false;
204  }
205 
206  // Check extras we integrate with. An extra always integrates with itself.
207  extra_type_list_iterate(extra_road_get(extra)->integrators, iextra)
208  {
209  if (BV_ISSET(extras_near, extra_index(iextra))) {
210  return true;
211  }
212  }
214 
215  // The extra may connect to adjacent land tiles.
216  return extra_has_flag(extra, EF_CONNECT_LAND) && terrain_near != T_UNKNOWN
217  && terrain_type_terrain_class(terrain_near) != TC_OCEAN;
218 }
219 
224 bool should_draw(const struct tileset *t, const extra_type *extra,
225  direction8 dir, const bv_extras extras,
226  const bv_extras extras_near, terrain *terrain_near)
227 {
228  // Only draw if the extra connects to the other tile.
229  if (!connects(t, extra, dir, extras, extras_near, terrain_near)) {
230  return false;
231  }
232 
233  // Only draw if the connection is not hidden by another extra.
234  extra_type_list_iterate(extra->hiders, hider)
235  {
236  if (connects(t, hider, dir, extras, extras_near, terrain_near)) {
237  return false;
238  }
239  }
241 
242  // Passes all the tests, draw it.
243  return true;
244 }
245 
253 std::tuple<std::bitset<DIR8_MAGIC_MAX>, bool>
254 road_data(const struct tileset *t, const tile *ptile,
255  const extra_type *extra)
256 {
257  const auto extras = *tile_extras(ptile);
258 
259  // Don't bother doing anything if the extra isn't on the tile in the first
260  // place.
261  if (!BV_ISSET(extras, extra_index(extra))) {
262  return make_tuple(std::bitset<DIR8_MAGIC_MAX>(), false);
263  }
264 
265  const auto terrain = tile_terrain(ptile);
266  struct terrain *terrain_near[DIR8_MAGIC_MAX] = {nullptr};
267  bv_extras extras_near[DIR8_MAGIC_MAX];
268  build_tile_data(ptile, terrain, terrain_near, extras_near);
269 
270  // Check connections to adjacent tiles.
271  std::bitset<DIR8_MAGIC_MAX> draw;
272  for (int i = 0; i < tileset_num_valid_dirs(t); ++i) {
273  auto dir = tileset_valid_dirs(t)[i];
274  draw[i] = should_draw(t, extra, static_cast<direction8>(dir), extras,
275  extras_near[dir], terrain_near[dir]);
276  }
277 
278  // Draw the isolated sprite if we don't draw anything in any direction and
279  // it's not hidden by something else.
280  auto isolated = !draw.any()
281  && (!tile_city(ptile) || !gui_options->draw_cities)
282  && !is_hidden(extra, extras);
283 
284  return make_tuple(draw, isolated);
285 }
286 
287 } // anonymous namespace
288 
292 std::vector<drawn_sprite>
293 layer_roads::fill_sprite_array(const tile *ptile, const tile_edge *pedge,
294  const tile_corner *pcorner,
295  const unit *punit) const
296 {
297  Q_UNUSED(pedge);
298  Q_UNUSED(pcorner);
299 
300  // Should we draw anything in the first place?
301  if (!ptile) {
302  return {};
303  }
304 
305  const auto terrain = tile_terrain(ptile);
306  if (!tile_terrain(ptile)) {
307  return {};
308  }
309 
310  // Now we draw
311  std::vector<drawn_sprite> sprs;
312 
313  // Loop over all extras (picking the data for the correct terrain) and draw
314  // them.
315  for (const auto &data : m_all_combined[terrain_index(terrain)]) {
317  fill_corners(sprs, data, ptile);
318  fill_all_combined(sprs, data, ptile);
319  }
320  }
321  for (const auto &data : m_all_separate[terrain_index(terrain)]) {
323  fill_corners(sprs, data, ptile);
324  fill_all_separate(sprs, data, ptile);
325  }
326  }
327  for (const auto &data : m_parity_combined[terrain_index(terrain)]) {
329  fill_corners(sprs, data, ptile);
330  fill_parity_combined(sprs, data, ptile);
331  }
332  }
333 
334  return sprs;
335 }
336 
341 void layer_roads::fill_corners(std::vector<drawn_sprite> &sprs,
342  const corner_sprites &data,
343  const tile *ptile) const
344 {
346  return;
347  }
348  if (tileset_hex_height(tileset()) != 0
349  || tileset_hex_width(tileset()) != 0) {
350  return;
351  }
352 
353  /* Roads going diagonally adjacent to this tile need to be
354  * partly drawn on this tile. */
355 
356  const auto terrain = tile_terrain(ptile);
357  struct terrain *terrain_near[DIR8_MAGIC_MAX] = {nullptr};
358  bv_extras extras_near[DIR8_MAGIC_MAX];
359  build_tile_data(ptile, terrain, terrain_near, extras_near);
360 
361  const auto num_valid_dirs = tileset_num_valid_dirs(tileset());
362  const auto valid_dirs = tileset_valid_dirs(tileset());
363 
364  /* Draw the corner sprite if:
365  * - There is a diagonal connection between two adjacent tiles.
366  * - There is no diagonal connection that intersects this connection.
367  *
368  * That is, if we are drawing tile X below:
369  *
370  * +--+--+
371  * |NW|N |
372  * +--+--+
373  * | W|X |
374  * +--+--+
375  *
376  * We draw a corner on X if:
377  * - W and N are connected;
378  * - X and NW are not.
379  * This gets a bit more complicated with hiders, thus we call
380  * should_draw().
381  */
382  for (int i = 0; i < num_valid_dirs; ++i) {
383  enum direction8 dir = valid_dirs[i];
384 
385  if (!is_cardinal_tileset_dir(tileset(), dir)) {
386  // Draw corner sprites for this non-cardinal direction (= NW on the
387  // schema).
388 
389  // = W on the schema
390  auto cardinal_before =
391  valid_dirs[(i + num_valid_dirs - 1) % num_valid_dirs];
392  // = N on the schema
393  auto cardinal_after = valid_dirs[(i + 1) % num_valid_dirs];
394 
395  /* should_draw() needs to know the direction in which the road would go
396  * to account for cardinal-only roads and hiders. We assume that all
397  * roads are bidirectional, that is going from N to W is the same as
398  * going from W to N. This may break with roads connecting to land,
399  * but it is a rare occurrence.
400  *
401  * We already know the non-cardinal direction where we do not want a
402  * connection (NW above). To compute the other one, remark that going
403  * from the W tile to the N tile is going NE, or N+1 counting
404  * clockwise. This is what we compute here: */
405  auto relative_dir = valid_dirs[(i + 2) % num_valid_dirs];
406 
407  if (data.corners[dir]
408  && !should_draw(tileset(), data.extra, dir, *tile_extras(ptile),
409  extras_near[dir], terrain_near[dir])
410  && should_draw(tileset(), data.extra, relative_dir,
411  extras_near[cardinal_before],
412  extras_near[cardinal_after],
413  terrain_near[cardinal_after])) {
414  sprs.emplace_back(tileset(), data.corners[dir]);
415  }
416  }
417  }
418 }
419 
425 void layer_roads::fill_all_combined(std::vector<drawn_sprite> &sprs,
426  const all_combined_data &data,
427  const tile *ptile) const
428 {
429  auto [draw, isolated] = road_data(tileset(), ptile, data.extra);
430 
431  // Draw the sprite
432  if (draw.any() || isolated) {
433  sprs.emplace_back(tileset(), data.sprites[draw.to_ulong()]);
434  }
435 }
436 
442 void layer_roads::fill_all_separate(std::vector<drawn_sprite> &sprs,
443  const all_separate_data &data,
444  const tile *ptile) const
445 {
446  auto [draw, isolated] = road_data(tileset(), ptile, data.extra);
447 
448  // Draw the sprites
449  for (int i = 0; i < tileset_num_valid_dirs(tileset()); ++i) {
450  if (draw[i]) {
451  sprs.emplace_back(tileset(), data.sprites[i]);
452  }
453  }
454 
455  // Draw the isolated sprite if needed
456  if (isolated) {
457  sprs.emplace_back(tileset(), data.isolated);
458  }
459 }
460 
467 void layer_roads::fill_parity_combined(std::vector<drawn_sprite> &sprs,
468  const parity_combined_data &data,
469  const tile *ptile) const
470 {
471  auto [draw, isolated] = road_data(tileset(), ptile, data.extra);
472 
473  // Pick up the sprites
474  int even_tileno = 0, odd_tileno = 0;
475  for (int i = 0; i < tileset_num_valid_dirs(tileset()) / 2; i++) {
476  auto even = 2 * i;
477  auto odd = 2 * i + 1;
478 
479  if (draw[even]) {
480  even_tileno |= 1 << i;
481  }
482  if (draw[odd]) {
483  odd_tileno |= 1 << i;
484  }
485  }
486 
487  // Draw the cardinal/even roads first
488  if (even_tileno != 0) {
489  sprs.emplace_back(tileset(), data.sprites.first[even_tileno]);
490  }
491  if (odd_tileno != 0) {
492  sprs.emplace_back(tileset(), data.sprites.second[odd_tileno]);
493  }
494 
495  // Draw the isolated sprite if needed
496  if (isolated) {
497  sprs.emplace_back(tileset(), data.isolated);
498  }
499 }
500 
502 {
503  m_all_combined.clear();
504  m_all_separate.clear();
505  m_parity_combined.clear();
506 }
507 
508 } // namespace freeciv
bool BV_ISSET(const BV &bv, int bit)
Definition: bitvector.h:37
std::vector< std::vector< all_combined_data > > m_all_combined
Definition: layer_roads.h:81
void fill_all_combined(std::vector< drawn_sprite > &sprs, const all_combined_data &data, const tile *ptile) const
Fill sprites for extras with type RoadAllCombined.
std::vector< std::vector< parity_combined_data > > m_parity_combined
Definition: layer_roads.h:83
layer_roads(struct tileset *ts)
Constructor.
Definition: layer_roads.cpp:31
void initialize_all_combined(all_combined_data &data, const QString &tag, const terrain *terrain)
Initializes sprite data for RoadAllCombined.
Definition: layer_roads.cpp:94
void reset_ruleset() override
Resets cached data that depends on the ruleset.
std::vector< std::vector< all_separate_data > > m_all_separate
Definition: layer_roads.h:82
void fill_all_separate(std::vector< drawn_sprite > &sprs, const all_separate_data &data, const tile *ptile) const
Fill sprites for extras with type RoadAllSeparate.
std::vector< drawn_sprite > fill_sprite_array(const tile *ptile, const tile_edge *pedge, const tile_corner *pcorner, const unit *punit) const override
Returns the sprites to draw roads.
void fill_corners(std::vector< drawn_sprite > &sprs, const corner_sprites &data, const tile *ptile) const
Fills "corner" sprites that help complete diagonal roads where they overlap with adjacent tiles.
void initialize_parity_combined(parity_combined_data &data, const QString &tag, const terrain *terrain)
Initializes sprite data for RoadAllSeparate.
void initialize_all_separate(all_separate_data &data, const QString &tag, const terrain *terrain)
Initializes sprite data for RoadAllSeparate.
void fill_parity_combined(std::vector< drawn_sprite > &sprs, const parity_combined_data &data, const tile *ptile) const
Fill sprites for extras with type RoadAllSeparate.
void initialize_corners(corner_sprites &data, const extra_type *extra, const QString &tag, const terrain *terrain)
Initializes "corner" sprite data.
Definition: layer_roads.cpp:73
void initialize_extra(const extra_type *extra, const QString &tag, extrastyle_id style) override
Collects all extras to be drawn.
Definition: layer_roads.cpp:39
A layer when drawing the map.
Definition: layer.h:153
QPixmap * load_sprite(const QStringList &possible_names, bool required=false, bool verbose=true) const
Shortcut to load a sprite from the tileset.
Definition: layer.cpp:79
struct tileset * tileset() const
Definition: layer.h:241
char * extras
Definition: comments.cpp:34
bool extra_has_flag(const struct extra_type *pextra, enum extra_flag_id flag)
Check if extra has given flag.
Definition: extras.cpp:779
#define extra_type_list_iterate(extralist, pextra)
Definition: extras.h:145
#define extra_index(_e_)
Definition: extras.h:163
#define extra_type_list_iterate_end
Definition: extras.h:147
#define extra_road_get(_e_)
Definition: extras.h:171
#define DIR8_MAGIC_MAX
Definition: fc_types.h:386
#define INIT(vector, func)
Definition: path.cpp:10
client_options * gui_options
Definition: options.cpp:74
bool is_cardinal_only_road(const struct extra_type *pextra)
Is extra cardinal only road.
Definition: road.cpp:421
bool draw_cities
Definition: options.h:136
struct extra_type_list * hiders
Definition: extras.h:125
Stores the data common to all road types.
Definition: layer_roads.h:20
std::array< QPixmap *, DIR8_MAGIC_MAX > corners
Definition: layer_roads.h:23
Definition: tile.h:42
Definition: unit.h:134
Terrain_type_id terrain_count()
Return the number of terrains.
Definition: terrain.cpp:93
Terrain_type_id terrain_index(const struct terrain *pterrain)
Return the terrain index.
Definition: terrain.cpp:110
enum terrain_class terrain_type_terrain_class(const struct terrain *pterrain)
What terrain class terrain type belongs to.
Definition: terrain.cpp:464
#define T_UNKNOWN
Definition: terrain.h:51
struct city * tile_city(const struct tile *ptile)
Return the city on this tile (or nullptr), checking for city center.
Definition: tile.cpp:72
#define tile_terrain(_tile)
Definition: tile.h:93
static const bv_extras * tile_extras(const struct tile *ptile)
Definition: tile.h:102
int tileset_hex_width(const struct tileset *t)
Return the hex_width of the current tileset.
Definition: tilespec.cpp:345
QString valid_index_str(const struct tileset *t, int idx)
Do the same thing as cardinal_str, except including all valid directions.
Definition: tilespec.cpp:2251
void build_tile_data(const struct tile *ptile, struct terrain *pterrain, struct terrain **tterrain_near, bv_extras *textras_near)
Assemble some data that is used in building the tile sprite arrays.
Definition: tilespec.cpp:3080
bool is_cardinal_tileset_dir(const struct tileset *t, enum direction8 dir)
Return TRUE iff the dir is cardinal in this tileset.
Definition: tilespec.cpp:675
int tileset_hex_height(const struct tileset *t)
Return the hex_height of the current tileset.
Definition: tilespec.cpp:351
int tileset_num_valid_dirs(const struct tileset *t)
Returns the number of valid directions in the tileset.
Definition: tilespec.cpp:582
bool is_extra_drawing_enabled(const extra_type *pextra)
Should the given extra be drawn FIXME: Some extras can not be switched.
Definition: tilespec.cpp:3133
std::array< direction8, 8 > tileset_valid_dirs(const struct tileset *t)
Returns the valid directions for the tileset.
Definition: tilespec.cpp:592
QStringList make_tag_terrain_list(const QString &prefix, const QString &suffix, const struct terrain *pterrain)
Make the list of possible tag names for the extras which may vary depending on the terrain they're on...
Definition: tilespec.cpp:2914
QString dir_get_tileset_name(enum direction8 dir)
Return the tileset name of the direction.
Definition: tilespec.cpp:613