Freeciv21
Develop your civilization from humble roots to a global empire
layer_terrain.cpp
Go to the documentation of this file.
1 /*__ ___ ***************************************
2 / \ / \ Copyright (c) 2021-2023 Freeciv21 contributors.
3 \_ \ / __/ This file is part of Freeciv21.
4  _\ \ / /__ Freeciv21 is free software: you can redistribute it
5  \___ \____/ __/ and/or modify it under the terms of the GNU General
6  \_ _/ Public License as published by the Free Software
7  | \ \ \_ Foundation, either version 3 of the License,
8  | or (at your option) any later version.
9  _/ /\ You should have received a copy of the GNU
10  /o) (o/\ \_ General Public License along with Freeciv21.
11  \_____/ / If not, see https://www.gnu.org/licenses/.
12  \____/ ********************************************************/
13 
14 #include "layer_terrain.h"
15 
16 #include "climap.h"
17 #include "extras.h"
18 #include "game.h" // For extra_type_iterate
19 #include "map.h"
20 #include "rand.h"
21 #include "sprite_g.h"
22 #include "tilespec.h"
23 
103 namespace {
104 static const char direction4letters[5] = "udrl";
105 }
106 
107 namespace freeciv {
108 
109 layer_terrain::layer_terrain(struct tileset *ts, int number)
110  : freeciv::layer(ts, LAYER_TERRAIN1), m_number(number)
111 {
112 }
113 
119 {
120  if (name.isEmpty()) {
122  _("[layer%d] match_types: names cannot be empty."),
123  m_number);
124  return false;
125  }
126 
127  if (m_matching_groups.count(name.at(0)) != 0) {
129  tileset(), LOG_ERROR,
130  _("[layer%d] match_types: \"%s\" initial ('%c') is not unique."),
131  m_number, qUtf8Printable(name), qUtf8Printable(name.at(0)));
132  return false;
133  }
134 
135  m_matching_groups[name.at(0)] =
136  matching_group{static_cast<int>(m_matching_groups.size()), name};
137 
138  return true;
139 }
140 
149 bool layer_terrain::add_tag(const QString &tag, const QString &sprite_name)
150 {
151  bool ok = true;
152  if (m_terrain_tag_info.count(tag) > 0) {
154  "Multiple [tile] sections containing terrain tag \"%s\".",
155  qUtf8Printable(tag));
156  ok = false;
157  }
158 
159  m_terrain_tag_info[tag].sprite_name = sprite_name; // Creates the data
160  return ok;
161 }
162 
168 bool layer_terrain::set_tag_sprite_type(const QString &tag, sprite_type type)
169 {
170  // FIXME The ancient code did not handle CELL_CORNER for "tall" terrain or
171  // sprite offsets. Does the new code work support that?
172  m_terrain_tag_info.at(tag).type = type;
173  return true;
174 }
175 
181 bool layer_terrain::set_tag_offsets(const QString &tag, int offset_x,
182  int offset_y)
183 {
184  m_terrain_tag_info.at(tag).offset_x = offset_x;
185  m_terrain_tag_info.at(tag).offset_y = offset_y;
186  return true;
187 }
188 
195  const QString &group_name)
196 {
197  if (auto g = group(group_name); g != nullptr) {
198  m_terrain_tag_info.at(tag).group = g;
199  // Tags always match with themselves
200  m_terrain_tag_info.at(tag).matches_with.push_back(g);
201  return true;
202  } else {
203  return false;
204  }
205 }
206 
212 bool layer_terrain::set_tag_matches_with(const QString &tag,
213  const QString &group_name)
214 {
215  if (auto g = group(group_name); g != nullptr) {
216  m_terrain_tag_info.at(tag).matches_with.push_back(g);
217  return true;
218  } else {
219  return false;
220  }
221 }
222 
226 void layer_terrain::enable_blending(const QString &tag)
227 {
228  if (!tileset_is_isometric(tileset())) {
230  _("Blending is only supported for isometric tilesets"));
231  return;
232  }
233  // Create the entry
234  m_terrain_tag_info.at(tag).blend = true;
235 }
236 
241 {
242  // Find the good terrain_info
243  terrain_info info;
244  if (m_terrain_tag_info.count(terrain->graphic_str) > 0) {
246  } else if (m_terrain_tag_info.count(terrain->graphic_alt) > 0) {
248  } else if (m_number == 0) {
249  // All terrains should be present in layer 0...
251  _("Terrain \"%s\": no graphic tile \"%s\" or \"%s\"."),
254  return;
255  } else {
256  // Terrain is not drawn by this layer
257  return;
258  }
259 
260  // Determine the match style
261  switch (info.matches_with.size()) {
262  case 0:
263  case 1:
264  info.style = MATCH_NONE;
265  break;
266  case 2:
267  if (info.matches_with.front() == info.matches_with.back()) {
268  info.style = MATCH_SAME;
269  } else {
270  info.style = MATCH_PAIR;
271  }
272  break;
273  default:
274  info.style = MATCH_FULL;
275  break;
276  }
277 
278  // Build graphics data
279  // TODO Use inheritance?
280  switch (info.type) {
281  case CELL_WHOLE:
282  switch (info.style) {
283  case MATCH_NONE:
285  break;
286  case MATCH_SAME:
288  break;
289  case MATCH_PAIR:
290  case MATCH_FULL:
292  tileset(), LOG_ERROR,
293  _("Not implemented CELL_WHOLE + MATCH_FULL/MATCH_PAIR."));
294  return;
295  }
296  break;
297  case CELL_CORNER:
298  switch (info.style) {
299  case MATCH_NONE:
301  break;
302  case MATCH_SAME:
304  break;
305  case MATCH_PAIR:
307  break;
308  case MATCH_FULL:
310  break;
311  }
312  break;
313  case CELL_HEX_CORNER:
316  _("Layer %d, tag \"%s\": Sprite type \"hex_corner\" is "
317  "only supported for isometric hexagonal tilesets"),
318  m_number, qUtf8Printable(info.sprite_name));
319  }
321  break;
322  }
323 
324  // Blending
326 
327  // Copy it to the terrain index
329 }
330 
335  terrain_info &info)
336 {
337  QString buffer;
338 
339  // Load whole sprites for this tile.
340  for (int i = 0;; i++) {
341  buffer = QStringLiteral("t.l%1.%2%3")
342  .arg(m_number)
343  .arg(info.sprite_name)
344  .arg(i + 1);
345  auto sprite = load_sprite({buffer});
346  if (!sprite) {
347  break;
348  }
349  info.sprites.push_back(sprite);
350  }
351 
352  // check for base sprite, allowing missing sprites above base
353  if (m_number == 0 && info.sprites.empty()) {
354  // TRANS: 'base' means 'base of terrain gfx', not 'military base'
356  _("Missing base sprite for tag \"%s\"."),
357  qUtf8Printable(buffer));
358  }
359 }
360 
365  terrain_info &info)
366 {
367  // Load 16 cardinally-matched sprites.
368  for (int i = 0; i < tileset_num_index_cardinals(tileset()); i++) {
369  auto buffer = QStringLiteral("t.l%1.%2_%3")
370  .arg(m_number)
371  .arg(info.sprite_name)
372  .arg(cardinal_index_str(tileset(), i));
373  info.sprites.push_back(tiles_lookup_sprite_tag_alt(
374  tileset(), LOG_ERROR, qUtf8Printable(buffer), "", "matched terrain",
375  terrain_rule_name(terrain), true));
376  }
377 }
378 
383  terrain_info &info)
384 {
385  // Determine how many sprites we need
386  int number = NUM_CORNER_DIRS;
387 
388  // Load the sprites
389  for (int i = 0; i < number; i++) {
390  enum direction4 dir = static_cast<direction4>(i % NUM_CORNER_DIRS);
391  auto buffer = QStringLiteral("t.l%1.%2_cell_%3")
392  .arg(m_number)
393  .arg(info.sprite_name)
394  .arg(direction4letters[dir]);
395  info.sprites.push_back(tiles_lookup_sprite_tag_alt(
396  tileset(), LOG_ERROR, qUtf8Printable(buffer), "", "cell terrain",
397  terrain_rule_name(terrain), true));
398  }
399 }
400 
405  terrain_info &info)
406 {
407  // Determine how many sprites we need
408  int number = NUM_CORNER_DIRS * 2 * 2 * 2;
409 
410  // Load the sprites
411  for (int i = 0; i < number; i++) {
412  enum direction4 dir = static_cast<direction4>(i % NUM_CORNER_DIRS);
413  int value = i / NUM_CORNER_DIRS;
414 
415  auto buffer = QStringLiteral("t.l%1.%2_cell_%3%4%5%6")
416  .arg(m_number)
417  .arg(info.sprite_name)
418  .arg(direction4letters[dir])
419  .arg(value & 1)
420  .arg((value >> 1) & 1)
421  .arg((value >> 2) & 1);
422  info.sprites.push_back(tiles_lookup_sprite_tag_alt(
423  tileset(), LOG_ERROR, qUtf8Printable(buffer), "",
424  "same cell terrain", terrain_rule_name(terrain), true));
425  }
426 }
427 
432  terrain_info &info)
433 {
434  // Determine how many sprites we need
435  int number = NUM_CORNER_DIRS * 2 * 2 * 2;
436 
437  // Load the sprites
438  for (int i = 0; i < number; i++) {
439  enum direction4 dir = static_cast<direction4>(i % NUM_CORNER_DIRS);
440  int value = i / NUM_CORNER_DIRS;
441 
442  QChar letters[2] = {info.group->name[0],
443  info.matches_with.back()->name[0]};
444  auto buffer = QStringLiteral("t.l%1.%2_cell_%3_%4_%5_%6")
445  .arg(m_number)
446  .arg(info.sprite_name, QChar(direction4letters[dir]),
447  letters[value & 1], letters[(value >> 1) & 1],
448  letters[(value >> 2) & 1]);
449 
450  info.sprites.push_back(tiles_lookup_sprite_tag_alt(
451  tileset(), LOG_ERROR, qUtf8Printable(buffer), "",
452  "cell pair terrain", terrain_rule_name(terrain), true));
453  }
454 }
455 
460  terrain_info &info)
461 {
462  // Determine how many sprites we need
463  // N directions (NSEW) * 3 dimensions of matching
464  // could use exp() or expi() here?
465  const int count = info.matches_with.size();
466  const int number = NUM_CORNER_DIRS * count * count * count;
467 
468  // Load the sprites
469  for (int i = 0; i < number; i++) {
470  enum direction4 dir = static_cast<direction4>(i % NUM_CORNER_DIRS);
471  int value = i / NUM_CORNER_DIRS;
472 
473  const auto g1 = info.matches_with[value % count];
474  value /= count;
475  const auto g2 = info.matches_with[value % count];
476  value /= count;
477  const auto g3 = info.matches_with[value % count];
478 
479  const matching_group *n, *s, *e, *w;
480  // Assume merged cells. This should be a separate option.
481  switch (dir) {
482  case DIR4_NORTH:
483  s = info.group;
484  w = g1;
485  n = g2;
486  e = g3;
487  break;
488  case DIR4_EAST:
489  w = info.group;
490  n = g1;
491  e = g2;
492  s = g3;
493  break;
494  case DIR4_SOUTH:
495  n = info.group;
496  e = g1;
497  s = g2;
498  w = g3;
499  break;
500  case DIR4_WEST:
501  default: // avoid warnings
502  e = info.group;
503  s = g1;
504  w = g2;
505  n = g3;
506  break;
507  };
508 
509  // Use first character of match_types, already checked for uniqueness.
510  auto buffer = QStringLiteral("t.l%1.cellgroup_%2_%3_%4_%5")
511  .arg(QString::number(m_number), n->name[0], e->name[0],
512  s->name[0], w->name[0]);
513 
514  if (auto sprite = load_sprite({buffer})) {
515  // Crop the sprite to separate this cell.
516  const int W = sprite->width();
517  const int H = sprite->height();
518  int x[4] = {W / 4, W / 4, 0, W / 2};
519  int y[4] = {H / 2, 0, H / 4, H / 4};
520  int xo[4] = {0, 0, -W / 2, W / 2};
521  int yo[4] = {H / 2, -H / 2, 0, 0};
522 
523  sprite = crop_sprite(sprite, x[dir], y[dir], W / 2, H / 2,
524  get_mask_sprite(tileset()), xo[dir], yo[dir]);
525  // We allocated new sprite with crop_sprite. Store its address so we
526  // can free it.
527  m_allocated.emplace_back(sprite);
528  info.sprites.push_back(sprite);
529  } else {
531  "Terrain graphics sprite for tag \"%s\" missing.",
532  qUtf8Printable(buffer));
533  }
534  }
535 }
536 
541  terrain_info &info)
542 {
543  // There are two sorts of sprites, which we call `left' and `right':
544  // ____
545  // .:..\...:.
546  // : R \__:_
547  // .:.../..:.
548  // _:__/ L :
549  // .:..\...:.
550  // : R \__:_
551  // .:.../..:.
552  // _:__/ :
553  //
554  // The `terrain' variable corresponds to the tile cut in two parts
555  // vertically (the ones with the L and R symbols in the drawing); the
556  // `right' and `left' terminology refers to that tile.
557  //
558  // For each terrain, we need sprites for left and right times the
559  // combinations for the two other tiles: for N matching groups, this is a
560  // total of 2 * N^3 sprites.
561 
562  // In order to render the edge of a tile in a variety of situations (the
563  // help dialog, the map edges, unknown tiles, ...), we need to cut each
564  // corner into its own pixmap. We do this for each terrain because we
565  // don't have access to other matching groups. This increases memory usage
566  // quite a bit, but it's the best one can do in the current framework. We
567  // use `mask.sprite' to produce the tile shape.
568 
569  // How to order the group indices when loading the sprite, depending on
570  // the direction. 0 is the terrain being loaded, 1 is the terrain for the
571  // direction before the corner if we count clockwise, and 2 is the terrain
572  // after the corner.
573  const int indices[6][3] = {
574  {2, 1, 0}, {0, 1, 2}, {1, 0, 2}, {2, 0, 1}, {0, 2, 1}, {1, 2, 0},
575  };
576 
577  // Load the sprites, create the cut sprites and store them in `info'. We
578  // fill `info.sprites' as a 3D array with the following indices:
579  // - corner index, clockwise from the top
580  // - match type "before" the corner
581  // - match type "after" it
582  // Since we only have a 1D vector, the array is unrolled into a single
583  // index.
584  for (int idir = 0; idir < 6; ++idir) {
585  // We start from NORTH, thus the first corner needs a `left' sprite.
586  // Afterwards it alternates
587  bool left = (idir % 2 == 0);
588 
589  for (int i = 0; i < info.matches_with.size(); ++i) {
590  for (int j = 0; j < info.matches_with.size(); ++j) {
591  auto buffer = QString();
592 
593  if (info.style == MATCH_SAME) {
594  std::array<int, 3> present = {
595  1, // Center tile
596  i, // "before"
597  j, // "after"
598  };
599 
600  buffer = QStringLiteral("t.l%1.%2_hex_cell_%3_%4_%5_%6")
601  .arg(m_number)
602  .arg(info.sprite_name)
603  .arg(left ? QStringLiteral("left")
604  : QStringLiteral("right"))
605  .arg(present[indices[idir][0]])
606  .arg(present[indices[idir][1]])
607  .arg(present[indices[idir][2]]);
608  } else {
609  std::array<matching_group *, 3> groups = {
610  info.group, // Center tile
611  info.matches_with[i], // "before"
612  info.matches_with[j], // "after"
613  };
614 
615  buffer = QStringLiteral("t.l%1.hex_cell_%2_%3_%4_%5")
616  .arg(m_number)
617  .arg(left ? QStringLiteral("left")
618  : QStringLiteral("right"))
619  .arg(groups[indices[idir][0]]->name[0])
620  .arg(groups[indices[idir][1]]->name[0])
621  .arg(groups[indices[idir][2]]->name[0]);
622  }
623 
624  if (auto sprite = load_sprite({buffer})) {
625  // Crop the sprite to separate this cell.
626  const int W = tileset_tile_width(tileset());
627  const int H = tileset_tile_height(tileset());
628  std::array<int, 6> y = {H / 4, 0, 0, 0, 0, H / 4};
629  std::array<int, 6> h = {H / 4, H / 2, H / 4, H / 4, H / 2, H / 4};
630  std::array<int, 6> xo = {-W / 2, -W / 2, -W / 2, 0, 0, 0};
631  std::array<int, 6> yo = {H / 4, -H / 4, -3 * H / 4,
632  -3 * H / 4, -H / 4, H / 4};
633 
634  sprite =
635  crop_sprite(sprite, 0, y[idir], W, h[idir],
636  get_mask_sprite(tileset()), xo[idir], yo[idir]);
637 
638  // We allocated a new sprite with crop_sprite. Store its address so
639  // we can free it.
640  m_allocated.emplace_back(sprite);
641  info.sprites.push_back(sprite);
642  } else {
644  "Terrain graphics sprite for tag \"%s\" missing.",
645  qUtf8Printable(buffer));
646  }
647  }
648  }
649  }
650 }
651 
656  terrain_info &info)
657 {
658  // Get the blending info
659  if (!info.blend) {
660  // No blending
661  return;
662  }
663 
664  // try an optional special name
665  auto buffer = QStringLiteral("t.blend.%1").arg(info.sprite_name);
666  auto blender = tiles_lookup_sprite_tag_alt(
667  tileset(), LOG_VERBOSE, qUtf8Printable(buffer), "", "blend terrain",
668  terrain_rule_name(terrain), true);
669 
670  if (blender == nullptr) {
671  // try an unloaded base name
672  // Need to pass "1" as an argument because %21 is interpreted as argument
673  // 21
674  buffer = QStringLiteral("t.l%1.%2%3")
675  .arg(m_number)
676  .arg(info.sprite_name)
677  .arg(1);
678  blender = tiles_lookup_sprite_tag_alt(
679  tileset(), LOG_ERROR, qUtf8Printable(buffer), "",
680  "base (blend) terrain", terrain_rule_name(terrain), true);
681  }
682 
683  if (blender == nullptr) {
685  tileset(), LOG_ERROR,
686  "Cannot find sprite for blending terrain with tag %s on layer %d",
687  qUtf8Printable(info.sprite_name), m_number);
688  return;
689  }
690 
691  // Set up blending sprites. This only works in iso-view!
692  const int W = tileset_tile_width(tileset());
693  const int H = tileset_tile_height(tileset());
694  const int offsets[4][2] = {{W / 2, 0}, {0, H / 2}, {W / 2, H / 2}, {0, 0}};
695  int dir = 0;
696 
697  for (; dir < 4; dir++) {
698  info.blend_sprites[dir] =
699  crop_sprite(blender, offsets[dir][0], offsets[dir][1], W / 2, H / 2,
700  get_dither_sprite(tileset()), 0, 0);
701  }
702 }
703 
707 std::vector<drawn_sprite>
709  const tile_corner *pcorner,
710  const unit *punit) const
711 {
712  if (ptile == nullptr) {
713  return {};
714  }
715 
716  const auto terrain = ptile->terrain;
717  if (terrain == nullptr) {
718  return {};
719  }
720 
721  // Don't draw terrain when the solid background is used
722  if (solid_background(ptile, punit, tile_city(ptile))) {
723  return {};
724  }
725 
726  auto sprites = std::vector<drawn_sprite>();
727 
728  // Handle scenario-defined sprites: scenarios can instruct the client to
729  // draw a specific sprite at some location.
730  // FIXME: this should avoid calling load_sprite since it's slow and
731  // increases the refcount without limit.
732  if (QPixmap * sprite;
733  ptile->spec_sprite && (sprite = load_sprite({ptile->spec_sprite}))) {
734  if (m_number == 0) {
735  sprites.emplace_back(tileset(), sprite);
736  }
737  // Skip the normal drawing process.
738  return sprites;
739  }
740 
741  struct terrain *terrain_near[8] = {nullptr};
742  bv_extras extras_near[8]; // dummy
743  build_tile_data(ptile, terrain, terrain_near, extras_near);
744 
745  fill_terrain_sprite_array(sprites, ptile, terrain, terrain_near);
746  fill_blending_sprite_array(sprites, ptile, terrain, terrain_near);
747 
748  return sprites;
749 }
750 
757 {
758  if (name.isEmpty() || m_matching_groups.count(name[0]) == 0) {
759  tileset_error(tileset(), LOG_ERROR, _("No matching group called %s"),
760  qUtf8Printable(name));
761  return nullptr;
762  }
763 
764  auto candidate = &m_matching_groups.at(name[0]);
765  if (candidate->name == name) {
766  return candidate;
767  } else {
768  // Should not happen
769  return nullptr;
770  }
771 }
772 
778 int layer_terrain::terrain_group(const terrain *pterrain) const
779 {
780  if (!pterrain || m_terrain_info.count(terrain_index(pterrain)) == 0) {
781  return -1;
782  }
783 
784  auto group = m_terrain_info.at(terrain_index(pterrain)).group;
785  if (!group) {
786  return -1;
787  }
788 
789  return group->number;
790 }
791 
792 namespace /* anonymous */ {
793 
801 template <std::size_t N>
802 int flattened_index(int size, const std::array<int, N> &indices,
803  int seed = 0)
804 {
805  int nested = seed;
806  for (auto it = indices.rbegin(); it != indices.rend(); ++it) {
807  nested *= size;
808  nested += *it;
809  }
810  return nested;
811 }
812 
813 } // anonymous namespace
814 
819  std::vector<drawn_sprite> &sprs, const tile *ptile,
820  const terrain *pterrain, terrain **tterrain_near) const
821 {
822  if (m_terrain_info.find(terrain_index(pterrain)) == m_terrain_info.end()) {
823  // Not drawn in this layer
824  return;
825  }
826 
827  const auto info = m_terrain_info.at(terrain_index(pterrain));
828 
829 #define MATCH(dir) terrain_group(tterrain_near[(dir)])
830 
831  switch (info.type) {
832  case CELL_WHOLE: {
833  switch (info.style) {
834  case MATCH_NONE: {
835  if (!info.sprites.empty()) {
836  /* Pseudo-random reproducable algorithm to pick a sprite. Use
837  * modulo to limit the number to a handleable size [0..32000). */
838  const int i = fc_randomly(std::abs(tile_index(ptile)) % 32000,
839  info.sprites.size());
840  if (Q_LIKELY(info.sprites[i] != nullptr)) {
841  sprs.emplace_back(tileset(), info.sprites[i], true, info.offset_x,
842  info.offset_y);
843  }
844  }
845  break;
846  }
847  case MATCH_SAME: {
848  fc_assert_ret(info.matches_with.size() == 2);
849  fc_assert_ret(info.sprites.size()
851  int tileno = 0;
852 
853  for (int i = 0; i < tileset_num_cardinal_dirs(tileset()); i++) {
854  enum direction8 dir = tileset_cardinal_dirs(tileset())[i];
855 
856  if (MATCH(dir) == info.matches_with.back()->number) {
857  tileno |= 1 << i;
858  }
859  }
860  if (Q_LIKELY(info.sprites[tileno] != nullptr)) {
861  sprs.emplace_back(tileset(), info.sprites[tileno], true,
862  info.offset_x, info.offset_y);
863  }
864  break;
865  }
866  case MATCH_PAIR:
867  case MATCH_FULL:
868  fc_assert(false); // not yet defined
869  break;
870  };
871  break;
872  }
873  case CELL_CORNER: {
874  /* Divide the tile up into four rectangular cells. Each of these
875  * cells covers one corner, and each is adjacent to 3 different
876  * tiles. For each cell we pick a sprite based upon the adjacent
877  * terrains at each of those tiles. Thus, we have 8 different sprites
878  * for each of the 4 cells (32 sprites total).
879  *
880  * These arrays correspond to the direction4 ordering. */
881  const int W = tileset_tile_width(tileset());
882  const int H = tileset_tile_height(tileset());
883  const int iso_offsets[4][2] = {
884  {W / 4, 0}, {W / 4, H / 2}, {W / 2, H / 4}, {0, H / 4}};
885  const int noniso_offsets[4][2] = {
886  {0, 0}, {W / 2, H / 2}, {W / 2, 0}, {0, H / 2}};
887 
888  // put corner cells
889  for (int i = 0; i < NUM_CORNER_DIRS; i++) {
890  const int count = info.matches_with.size();
891  enum direction8 dir = dir_ccw(DIR4_TO_DIR8[i]);
892  int x = (tileset_is_isometric(tileset()) ? iso_offsets[i][0]
893  : noniso_offsets[i][0]);
894  int y = (tileset_is_isometric(tileset()) ? iso_offsets[i][1]
895  : noniso_offsets[i][1]);
896  int m[3] = {MATCH(dir_ccw(dir)), MATCH(dir), MATCH(dir_cw(dir))};
897 
898  int array_index = 0;
899  switch (info.style) {
900  case MATCH_NONE:
901  // We have no need for matching, just plug the piece in place.
902  break;
903  case MATCH_SAME:
904  fc_assert_ret(info.matches_with.size() == 2);
905  array_index = flattened_index<3>(2, {(m[0] != info.group->number),
906  (m[1] != info.group->number),
907  (m[2] != info.group->number)});
908  break;
909  case MATCH_PAIR: {
910  fc_assert_ret(info.matches_with.size() == 2);
911  const auto that = info.matches_with.back()->number;
912  array_index = flattened_index<3>(
913  2, {(m[0] == that), (m[1] == that), (m[2] == that)});
914  } break;
915  case MATCH_FULL:
916  default: {
917  std::array<int, 3> n;
918  for (int j = 0; j < 3; j++) {
919  for (int k = 0; k < count; k++) {
920  n[j] = k; // default to last entry
921  if (m[j] == info.matches_with[k]->number) {
922  break;
923  }
924  }
925  }
926  array_index = flattened_index(count, n);
927  } break;
928  };
929  array_index = array_index * NUM_CORNER_DIRS + i;
930 
931  const auto sprite = info.sprites[array_index];
932  if (sprite) {
933  sprs.emplace_back(tileset(), sprite, true, x, y);
934  }
935  }
936  break;
937  }
938  case CELL_HEX_CORNER: {
939  const int W = tileset_tile_width(tileset());
940  const int H = tileset_tile_height(tileset());
941 
942  // The directions of the terrain "after" (see initialize_cell_hex_corner)
943  const std::array<direction8, 6> iso_dirs = {
944  DIR8_NORTH, DIR8_EAST, DIR8_SOUTHEAST,
945  DIR8_SOUTH, DIR8_WEST, DIR8_NORTHWEST,
946  };
947  // Where to put the cut sprites inside the tile area
948  const int iso_offsets[6][2] = {
949  {W / 2, 0}, {W / 2, H / 4}, {W / 2, 3 * H / 4},
950  {0, 3 * H / 4}, {0, H / 4}, {0, 0},
951  };
952 
953  // Iterate over corners
954  for (int i = 0; i < 6; ++i) {
955  std::array<int, 2> matches = {
956  MATCH(iso_dirs[(i + 5) % 6]), // Direction "before"
957  MATCH(iso_dirs[i])}; // Direction "after"
958 
959  std::array<int, 2> indices = {0, 0};
960  switch (info.style) {
961  case MATCH_SAME:
962  fc_assert_ret(info.matches_with.size() == 2);
963  indices = {(matches[0] == info.group->number),
964  (matches[1] == info.group->number)};
965  break;
966  case MATCH_NONE:
967  case MATCH_PAIR:
968  case MATCH_FULL:
969  // Resolve the matching groups as indices in matches_with
970  for (int j = 0; j < 2; ++j) {
971  if (matches[j] < 0) {
972  // Unknown tile or edge of the map, pretend current terrain
973  // continues (it's always at match index 0)
974  indices[j] = 0;
975  continue;
976  }
977  auto it =
978  std::find_if(info.matches_with.begin(),
979  info.matches_with.end(), [=](const auto &group) {
980  return group->number == matches[j];
981  });
982  if (it == info.matches_with.end()) {
983  // Not matching against this terrain, pretend current terrain
984  // continues (it's always at match index 0)
985  indices[j] = 0;
986  } else {
987  indices[j] = std::distance(info.matches_with.begin(), it);
988  }
989  }
990  }
991 
992  // Pick the sprite
993  int array_index = flattened_index<2>(info.matches_with.size(),
994  {indices[1], indices[0]}, i);
995 
996  const auto sprite = info.sprites[array_index];
997  if (sprite) {
998  sprs.emplace_back(tileset(), sprite, true, iso_offsets[i][0],
999  iso_offsets[i][1]);
1000  }
1001  }
1002  }
1003 
1004  break;
1005  }
1006 #undef MATCH
1007 }
1008 
1016  std::vector<drawn_sprite> &sprs, const tile *ptile,
1017  const terrain *pterrain, terrain **tterrain_near) const
1018 {
1019  // By how much the blending sprites need to be offset
1020  const int W = tileset_tile_width(tileset());
1021  const int H = tileset_tile_height(tileset());
1022  const int offsets[4][2] = {{W / 2, 0}, {0, H / 2}, {W / 2, H / 2}, {0, 0}};
1023 
1024  // Not drawn
1025  if (m_terrain_info.count(terrain_index(pterrain)) == 0) {
1026  return;
1027  }
1028 
1029  // Not blended
1030  auto info = m_terrain_info.at(terrain_index(pterrain));
1031  if (!info.blend) {
1032  return;
1033  }
1034 
1035  for (int dir = 0; dir < 4; dir++) {
1036  struct tile *neighbor = mapstep(&(wld.map), ptile, DIR4_TO_DIR8[dir]);
1037  struct terrain *other;
1038 
1039  // No other tile, don't "blend" at the edge of the map
1040  if (!neighbor) {
1041  continue;
1042  }
1043 
1044  // Other tile is unknown
1045  if (client_tile_get_known(neighbor) == TILE_UNKNOWN) {
1046  continue;
1047  }
1048 
1049  // No blending between identical terrains
1050  if (pterrain == (other = tterrain_near[DIR4_TO_DIR8[dir]])) {
1051  continue;
1052  }
1053 
1054  // Other terrain is not drawn
1055  if (m_terrain_info.count(terrain_index(other)) == 0) {
1056  continue;
1057  }
1058 
1059  // Other terrain is not blended
1060  auto other_info = m_terrain_info.at(terrain_index(other));
1061  if (!other_info.blend) {
1062  continue;
1063  }
1064 
1065  // Pick the blending sprite and add it
1066  if (Q_LIKELY(other_info.blend_sprites.at(dir) != nullptr)) {
1067  sprs.emplace_back(tileset(), other_info.blend_sprites.at(dir), true,
1068  offsets[dir][0], offsets[dir][1]);
1069  }
1070  }
1071 }
1072 
1073 } // namespace freeciv
QPixmap crop_sprite(const QPixmap *sprite)
Helper function to crop a sprite.
Definition: canvas.cpp:115
bool add_tag(const QString &tag, const QString &sprite_name)
Makes a terrain tag available for use by this layer.
matching_group * group(const QString &name)
Retrieves the group structure of the provided name.
int terrain_group(const terrain *pterrain) const
Retrieves the group number for a given terrain.
void fill_blending_sprite_array(std::vector< drawn_sprite > &sprs, const tile *ptile, const terrain *pterrain, terrain **tterrain_near) const
Helper function for fill_sprite_array.
void initialize_cell_corner_match_none(const terrain *terrain, terrain_info &info)
Sets up terrain information for CELL_CORNER and MATCH_NONE.
void initialize_cell_corner_match_full(const terrain *terrain, terrain_info &info)
Sets up terrain information for CELL_CORNER and MATCH_FULL.
bool set_tag_matches_with(const QString &tag, const QString &group_name)
Sets the specified tag to be matched against a group.
std::map< int, terrain_info > m_terrain_info
Every terrain drawn in this layer appears here.
bool set_tag_offsets(const QString &tag, int offset_x, int offset_y)
Sets the offsets used to draw the specified tag.
layer_terrain(struct tileset *ts, int number)
sprite_type
Indicates how many sprites are used to draw a tile.
Definition: layer_terrain.h:30
@ CELL_WHOLE
One sprite for the entire tile.
Definition: layer_terrain.h:31
@ CELL_HEX_CORNER
One sprite for each hexagonal corner of the tile.
Definition: layer_terrain.h:33
@ CELL_CORNER
One sprite for each corner of the tile.
Definition: layer_terrain.h:32
bool set_tag_sprite_type(const QString &tag, sprite_type type)
Sets the type of sprite used to draw the specified tag.
void fill_terrain_sprite_array(std::vector< drawn_sprite > &sprs, const tile *ptile, const terrain *pterrain, terrain **tterrain_near) const
Helper function for fill_sprite_array.
void initialize_cell_whole_match_none(const terrain *terrain, terrain_info &info)
Sets up terrain information for CELL_WHOLE and MATCH_SAME.
void initialize_cell_corner_match_pair(const terrain *terrain, terrain_info &info)
Sets up terrain information for CELL_CORNER and MATCH_PAIR.
void enable_blending(const QString &tag)
Enable blending on this layer for the given terrain tag.
void initialize_cell_corner_match_same(const terrain *terrain, terrain_info &info)
Sets up terrain information for CELL_CORNER and MATCH_SAME.
std::map< QString, terrain_info > m_terrain_tag_info
Before terrains are loaded, this contains the list of available terrain tags.
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 list of sprites drawn by this layer somewhere on the map.
void initialize_terrain(const terrain *terrain) override
Sets up the structure to draw the specified terrain.
void initialize_cell_whole_match_same(const terrain *terrain, terrain_info &info)
Sets up terrain information for CELL_WHOLE and MATCH_SAME.
std::vector< std::unique_ptr< QPixmap > > m_allocated
List of those sprites in 'cells' that are allocated by some other means than load_sprite() and thus a...
void initialize_blending(const terrain *terrain, terrain_info &info)
Initializes blending sprites.
bool set_tag_matching_group(const QString &tag, const QString &group_name)
Sets the matching group for the specified tag.
std::map< QChar, matching_group > m_matching_groups
void initialize_cell_hex_corner(const terrain *terrain, terrain_info &info)
Sets up terrain information for CELL_HEX_CORNER.
bool create_matching_group(const QString &name)
Creates a matching group with the given name.
A layer when drawing the map.
Definition: layer.h:153
mapview_layer type() const
Definition: layer.h:238
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
bool solid_background(const tile *ptile, const unit *punit, const city *pcity) const
Whether a solid background should be drawn on a tile instead of its terrain.
Definition: layer.cpp:65
struct tileset * tileset() const
Definition: layer.h:241
enum known_type client_tile_get_known(const struct tile *ptile)
A tile's "known" field is used by the server to store whether each player knows the tile.
Definition: climap.cpp:29
#define _(String)
Definition: fcintl.h:50
struct world wld
Definition: game.cpp:48
const char * name
Definition: inputfile.cpp:118
#define MATCH(dir)
#define fc_assert_ret(condition)
Definition: log.h:112
constexpr auto LOG_ERROR
Definition: log.h:23
constexpr auto LOG_VERBOSE
Definition: log.h:26
#define fc_assert(condition)
Definition: log.h:89
constexpr auto LOG_WARN
Definition: log.h:24
enum direction8 dir_ccw(enum direction8 dir)
Returns the next direction counter-clock-wise.
Definition: map.cpp:1141
enum direction8 dir_cw(enum direction8 dir)
Returns the next direction clock-wise.
Definition: map.cpp:1112
struct tile * mapstep(const struct civ_map *nmap, const struct tile *ptile, enum direction8 dir)
Step from the given tile in the given direction.
Definition: map.cpp:342
Definition: path.cpp:10
#define fc_randomly(_seed, _size)
Definition: rand.h:33
size_t size
Definition: specvec.h:64
std::vector< matching_group * > matches_with
Definition: layer_terrain.h:56
std::array< QPixmap *, 4 > blend_sprites
Definition: layer_terrain.h:61
std::vector< QPixmap * > sprites
Definition: layer_terrain.h:58
char graphic_alt[MAX_LEN_NAME]
Definition: terrain.h:175
char graphic_str[MAX_LEN_NAME]
Definition: terrain.h:174
Definition: tile.h:42
char * spec_sprite
Definition: tile.h:58
struct terrain * terrain
Definition: tile.h:49
struct named_sprites sprites
Definition: tilespec.cpp:271
Definition: unit.h:134
struct civ_map map
Definition: world_object.h:21
const char * terrain_rule_name(const struct terrain *pterrain)
Return the (untranslated) rule name of the terrain.
Definition: terrain.cpp:184
Terrain_type_id terrain_index(const struct terrain *pterrain)
Return the terrain index.
Definition: terrain.cpp:110
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_index(_pt_)
Definition: tile.h:70
@ TILE_UNKNOWN
Definition: tile.h:29
int tileset_num_cardinal_dirs(const struct tileset *t)
Returns the number of cardinal directions used by the tileset.
Definition: tilespec.cpp:554
std::array< direction8, 8 > tileset_cardinal_dirs(const struct tileset *t)
Returns the cardinal directions used by the tileset.
Definition: tilespec.cpp:574
int tileset_topo_index(const struct tileset *t)
Return tileset topology index.
Definition: tilespec.cpp:3854
bool tileset_is_isometric(const struct tileset *t)
Return whether the current tileset is isometric.
Definition: tilespec.cpp:336
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
const QPixmap * get_mask_sprite(const struct tileset *t)
Return tile mask sprite.
Definition: tilespec.cpp:3486
const QPixmap * get_dither_sprite(const struct tileset *t)
Return dither sprite.
Definition: tilespec.cpp:3478
int tileset_tile_height(const struct tileset *t)
Return the tile height of the current tileset.
Definition: tilespec.cpp:383
QPixmap * tiles_lookup_sprite_tag_alt(struct tileset *t, QtMsgType level, const char *tag, const char *alt, const char *what, const char *name, bool scale)
Lookup sprite to match tag, or else to match alt if don't find, or else return nullptr,...
Definition: tilespec.cpp:2724
void tileset_error(struct tileset *t, QtMsgType level, const char *format,...)
Called when ever there's problem in ruleset/tileset compatibility.
Definition: tilespec.cpp:291
QString cardinal_index_str(const struct tileset *t, int idx)
Return a directional string for the cardinal directions.
Definition: tilespec.cpp:2233
int tileset_num_index_cardinals(const struct tileset *t)
Returns the number of cardinal indices used by the tileset.
Definition: tilespec.cpp:564
int tileset_tile_width(const struct tileset *t)
Return the tile width of the current tileset.
Definition: tilespec.cpp:371
direction4
Definition: tilespec.h:51
@ DIR4_EAST
Definition: tilespec.h:51
@ DIR4_NORTH
Definition: tilespec.h:51
@ DIR4_SOUTH
Definition: tilespec.h:51
@ DIR4_WEST
Definition: tilespec.h:51
#define TS_TOPO_ISOHEX
Definition: tilespec.h:307
#define NUM_CORNER_DIRS
Definition: tilespec.h:44
constexpr direction8 DIR4_TO_DIR8[4]
Definition: tilespec.h:53