The Battle for Wesnoth  1.19.7+dev
builder.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2004 - 2024
3  by Philippe Plantier <ayin@anathas.org>
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 /**
17  * @file
18  * Terrain builder.
19  */
20 
21 #include "terrain/builder.hpp"
22 
23 #include "game_config_view.hpp"
24 #include "global.hpp"
26 #include "log.hpp"
27 #include "map/map.hpp"
28 #include "picture.hpp"
31 
32 static lg::log_domain log_engine("engine");
33 #define ERR_NG LOG_STREAM(err, log_engine)
34 #define WRN_NG LOG_STREAM(warn, log_engine)
35 
36 using namespace std::chrono_literals;
37 
38 /**
39  *
40  * These legacy map_location functions moved here from map_location.?pp.
41  * We have refactored them out of everything but this class. Hopefully
42  * the end is near...
43  *
44  // Adds an absolute location to a "delta" location
45  // This is not the mathematically correct behavior, it is neither
46  // commutative nor associative. Negative coordinates may give strange
47  // results. It is retained because terrain builder code relies in this
48  // broken behavior. Best avoid.
49  map_location legacy_negation() const;
50  map_location legacy_sum(const map_location &a) const;
51  map_location& legacy_sum_assign(const map_location &a);
52  map_location legacy_difference(const map_location &a) const;
53  *
54  */
55 
57 {
58  return map_location(-me.x, -me.y);
59 }
60 
62 {
63  bool parity = (me.x & 1) != 0;
64  me.x += a.x;
65  me.y += a.y;
66  if((a.x > 0) && (a.x % 2) && parity)
67  me.y++;
68  if((a.x < 0) && (a.x % 2) && !parity)
69  me.y--;
70 
71  return me;
72 }
73 
74 static map_location legacy_sum(const map_location& me, const map_location& a)
75 {
76  map_location ret(me);
77  legacy_sum_assign(ret, a);
78  return ret;
79 }
80 
82 {
83  return legacy_sum(me, legacy_negation(a));
84 }
85 
86 /**
87  *
88  * This file holds the terrain_builder implementation.
89  *
90  */
91 
92 void terrain_builder::tile::rebuild_cache(const std::string& tod, logs* log)
93 {
94  images_background.clear();
95  images_foreground.clear();
96 
97  if(!sorted_images) {
98  // sort images by their layer (and basey)
99  // but use stable to keep the insertion order in equal cases
100  std::stable_sort(images.begin(), images.end());
101  sorted_images = true;
102  }
103 
104  for(const rule_image_rand& ri : images) {
105  bool is_background = ri->is_background();
106  bool animate = (!ri.ri->is_water || prefs::get().animate_water());
107 
108  imagelist& img_list = is_background ? images_background : images_foreground;
109 
110  for(const rule_image_variant& variant : ri->variants) {
111  if(!variant.has_flag.empty()) {
112  bool has_flag_match = true;
113  for(const std::string& s : variant.has_flag) {
114  // If a flag listed in "has_flag" is not present, this variant does not match
115  if(flags.find(s) == flags.end()) {
116  has_flag_match = false;
117  break;
118  }
119  }
120 
121  if(!has_flag_match) {
122  continue;
123  }
124  }
125 
126  if(!variant.tods.empty() && variant.tods.find(tod) == variant.tods.end())
127  continue;
128 
129  // need to break parity pattern in RNG
130  /** @todo improve this */
131  unsigned int rnd = ri.rand / 7919; // just the 1000th prime
132  const animated<image::locator>& anim = variant.images[rnd % variant.images.size()];
133 
134  bool is_empty = true;
135  for(std::size_t i = 0; i < anim.get_frames_count(); ++i) {
136  if(!image::is_empty_hex(anim.get_frame(i))) {
137  is_empty = false;
138  break;
139  }
140  }
141 
142  if(is_empty)
143  continue;
144 
145  img_list.push_back(anim);
146 
147  assert(anim.get_animation_duration() != 0ms);
148 
149  if(variant.random_start < 0ms) {
150  img_list.back().set_animation_time(std::chrono::milliseconds{ri.rand} % img_list.back().get_animation_duration());
151  } else if(variant.random_start > 0ms) {
152  img_list.back().set_animation_time(std::chrono::milliseconds{ri.rand} % variant.random_start);
153  }
154 
155  if(!animate) {
156  img_list.back().pause_animation();
157  }
158 
159  if(log) {
160  log->emplace_back(&ri, &variant);
161  }
162 
163  break; // found a matching variant
164  }
165  }
166 }
167 
169 {
170  flags.clear();
171  images.clear();
172  sorted_images = false;
173  images_foreground.clear();
174  images_background.clear();
175  last_tod = "invalid_tod";
176 }
177 
178 static unsigned int get_noise(const map_location& loc, unsigned int index)
179 {
180  unsigned int a = (loc.x + 92872973) ^ 918273;
181  unsigned int b = (loc.y + 1672517) ^ 128123;
182  unsigned int c = (index + 127390) ^ 13923787;
183  unsigned int abc = a * b * c + a * b + b * c + a * c + a + b + c;
184  return abc * abc;
185 }
186 
188  : tiles_((x + 4) * (y + 4))
189  , x_(x)
190  , y_(y)
191 {
192  reset();
193 }
194 
196 {
197  for(std::vector<tile>::iterator it = tiles_.begin(); it != tiles_.end(); ++it)
198  it->clear();
199 }
200 
202 {
203  x_ = x;
204  y_ = y;
205  std::vector<terrain_builder::tile> new_tiles(static_cast<size_t>(x + 4) * (y + 4));
206  tiles_.swap(new_tiles);
207  reset();
208 }
209 
211 {
212  if(loc.x < -2 || loc.y < -2 || loc.x > (x_ + 1) || loc.y > (y_ + 1)) {
213  return false;
214  }
215 
216  return true;
217 }
218 
220 {
221  assert(on_map(loc));
222 
223  return tiles_[(loc.x + 2) + (loc.y + 2) * (x_ + 4)];
224 }
225 
227 {
228  assert(on_map(loc));
229 
230  return tiles_[(loc.x + 2) + (loc.y + 2) * (x_ + 4)];
231 }
232 
233 terrain_builder::terrain_builder(const config& level, const gamemap* m, const std::string& offmap_image, bool draw_border)
235  , map_(m)
236  , tile_map_(m ? map().w() : 0, m ? map().h() : 0)
237  , terrain_by_type_()
238  , draw_border_(draw_border)
239 {
240  image::precache_file_existence("terrain/");
241 
242  if(building_rules_.empty() && rules_cfg_) {
243  // off_map first to prevent some default rule seems to block it
244  add_off_map_rule(offmap_image);
245  // parse global terrain rules
247  } else {
248  // use cached global rules but clear local rules
250  }
251 
252  // parse local rules
254 
255  if(m)
256  build_terrains();
257 }
258 
260 {
261  for(int x = -2; x <= map().w(); ++x) {
262  for(int y = -2; y <= map().h(); ++y) {
263  tile_map_[map_location(x, y)].rebuild_cache("");
264  }
265  }
266 }
267 
269 {
271  for(; i != building_rules_.end();) {
272  if(i->local)
273  building_rules_.erase(i++);
274  else
275  ++i;
276  }
277 }
278 
280 {
281  rules_cfg_ = &cfg;
282  // use the swap trick to clear the rules cache and get a fresh one.
283  // because simple clear() seems to cause some progressive memory degradation.
284  building_ruleset empty;
285  std::swap(building_rules_, empty);
286 }
287 
289 {
290  tile_map_.reload(map().w(), map().h());
291  terrain_by_type_.clear();
292  build_terrains();
293 }
294 
296 {
297  map_ = m;
298  reload_map();
299 }
300 
302  const map_location& loc, const std::string& tod, const TERRAIN_TYPE terrain_type)
303 {
304  if(!tile_map_.on_map(loc))
305  return nullptr;
306 
307  tile& tile_at = tile_map_[loc];
308 
309  if(tod != tile_at.last_tod) {
310  tile_at.rebuild_cache(tod);
311  tile_at.last_tod = tod;
312  }
313 
314  const imagelist& img_list = (terrain_type == BACKGROUND) ? tile_at.images_background : tile_at.images_foreground;
315 
316  if(!img_list.empty()) {
317  return &img_list;
318  }
319 
320  return nullptr;
321 }
322 
324 {
325  if(!tile_map_.on_map(loc))
326  return false;
327 
328  bool changed = false;
329 
330  tile& btile = tile_map_[loc];
331 
333  if(a.need_update())
334  changed = true;
336  }
338  if(a.need_update())
339  changed = true;
341  }
342 
343  return changed;
344 }
345 
346 /** @todo TODO: rename this function */
348 {
349  if(tile_map_.on_map(loc)) {
350  tile& btile = tile_map_[loc];
351  // btile.images.clear();
352  btile.images_foreground.clear();
353  btile.images_background.clear();
354  const std::string filename = map().get_terrain_info(loc).minimap_image();
355 
356  if(!filename.empty()) {
357  animated<image::locator> img_loc;
358  img_loc.add_frame(100ms, image::locator("terrain/" + filename + ".png"));
359  img_loc.start_animation(0ms, true);
360  btile.images_background.push_back(img_loc);
361  }
362 
363  // Combine base and overlay image if necessary
364  if(map().get_terrain_info(loc).is_combined()) {
365  const std::string filename_ovl = map().get_terrain_info(loc).minimap_image_overlay();
366 
367  if(!filename_ovl.empty()) {
368  animated<image::locator> img_loc_ovl;
369  img_loc_ovl.add_frame(100ms, image::locator("terrain/" + filename_ovl + ".png"));
370  img_loc_ovl.start_animation(0ms, true);
371  btile.images_background.push_back(img_loc_ovl);
372  }
373  }
374  }
375 }
376 
378 {
379  tile_map_.reset();
380  terrain_by_type_.clear();
381  build_terrains();
382 }
383 
384 static bool image_exists(const std::string& name)
385 {
386  bool precached = name.find("..") == std::string::npos;
387 
388  if(precached && image::precached_file_exists(name)) {
389  return true;
390  } else if(image::exists(name)) {
391  return true;
392  }
393 
394  return false;
395 }
396 
397 static std::vector<std::string> get_variations(const std::string& base, const std::string& variations)
398 {
399  /** @todo optimize this function */
400  std::vector<std::string> res;
401  if(variations.empty()) {
402  res.push_back(base);
403  return res;
404  }
405  std::string::size_type pos = base.find("@V", 0);
406  if(pos == std::string::npos) {
407  res.push_back(base);
408  return res;
409  }
410  std::vector<std::string> vars = utils::split(variations, ';', 0);
411 
412  for(const std::string& v : vars) {
413  res.push_back(base);
414  pos = 0;
415  while((pos = res.back().find("@V", pos)) != std::string::npos) {
416  res.back().replace(pos, 2, v);
417  pos += v.size();
418  }
419  }
420  return res;
421 }
422 
424 {
425  // If the rule has no constraints, it is invalid
426  if(rule.constraints.empty())
427  return false;
428 
429  // Parse images and animations data
430  // If one is not valid, return false.
431  for(terrain_constraint& constraint : rule.constraints) {
432  for(rule_image& ri : constraint.images) {
433  for(rule_image_variant& variant : ri.variants) {
434  std::vector<std::string> var_strings = get_variations(variant.image_string, variant.variations);
435  for(const std::string& var : var_strings) {
436  /** @todo improve this, 99% of terrains are not animated. */
437  std::vector<std::string> frames = utils::square_parenthetical_split(var, ',');
439 
440  for(const std::string& frame : frames) {
441  const std::vector<std::string> items = utils::split(frame, ':');
442  const std::string& str = items.front();
443 
444  const std::size_t tilde = str.find('~');
445  bool has_tilde = tilde != std::string::npos;
446  const std::string filename = "terrain/" + (has_tilde ? str.substr(0, tilde) : str);
447 
448  if(!image_exists(filename)) {
449  continue; // ignore missing frames
450  }
451 
452  const std::string modif = (has_tilde ? str.substr(tilde + 1) : "");
453 
454  auto time = 100ms;
455  if(items.size() > 1) {
456  try {
457  time = std::chrono::milliseconds{std::stoi(items.back())};
458  } catch(const std::invalid_argument&) {
459  ERR_NG << "Invalid 'time' value in terrain image builder: " << items.back();
460  }
461  }
462  if(ri.global_image) {
463  res.add_frame(time, image::locator(filename, constraint.loc, ri.center_x, ri.center_y, modif));
464  } else {
465  res.add_frame(time, image::locator(filename, modif));
466  }
467  }
468  if(res.get_frames_count() == 0)
469  break; // no valid images, don't register it
470 
471  res.start_animation(0ms, true);
472  variant.images.push_back(std::move(res));
473  }
474  if(variant.images.empty())
475  return false; // no valid images, rule is invalid
476  }
477  }
478  }
479 
480  return true;
481 }
482 
484 {
485  static const struct
486  {
487  int ii;
488  int ij;
489  int ji;
490  int jj;
491  } rotations[6] {{1, 0, 0, 1}, {1, 1, -1, 0}, {0, 1, -1, -1}, {-1, 0, 0, -1}, {-1, -1, 1, 0}, {0, -1, 1, 1}};
492 
493  // The following array of matrices is intended to rotate the (x,y)
494  // coordinates of a point in a wesnoth hex (and wesnoth hexes are not
495  // regular hexes :) ).
496  // The base matrix for a 1-step rotation with the wesnoth tile shape
497  // is:
498  //
499  // r = s^-1 * t * s
500  //
501  // with s = [[ 1 0 ]
502  // [ 0 -sqrt(3)/2 ]]
503  //
504  // and t = [[ -1/2 sqrt(3)/2 ]
505  // [ -sqrt(3)/2 1/2 ]]
506  //
507  // With t being the rotation matrix (pi/3 rotation), and s a matrix
508  // that transforms the coordinates of the wesnoth hex to make them
509  // those of a regular hex.
510  //
511  // (demonstration left as an exercise for the reader)
512  //
513  // So we have
514  //
515  // r = [[ 1/2 -3/4 ]
516  // [ 1 1/2 ]]
517  //
518  // And the following array contains I(2), r, r^2, r^3, r^4, r^5
519  // (with r^3 == -I(2)), which are the successive rotations.
520  static const struct
521  {
522  double xx;
523  double xy;
524  double yx;
525  double yy;
526  } xyrotations[6] {
527  { 1., 0., 0., 1. },
528  { 1./2. , -3./4., 1., 1./2. },
529  { -1./2., -3./4., 1, -1./2.},
530  { -1. , 0., 0., -1. },
531  { -1./2., 3./4., -1., -1./2.},
532  { 1./2. , 3./4., -1., 1./2. },
533  };
534 
535  assert(angle >= 0);
536 
537  angle %= 6;
538 
539  // Vector i is going from n to s, vector j is going from ne to sw.
540  int vi = ret.loc.y - ret.loc.x / 2;
541  int vj = ret.loc.x;
542 
543  int ri = rotations[angle].ii * vi + rotations[angle].ij * vj;
544  int rj = rotations[angle].ji * vi + rotations[angle].jj * vj;
545 
546  ret.loc.x = rj;
547  ret.loc.y = ri + (rj >= 0 ? rj / 2 : (rj - 1) / 2);
548 
549  for(rule_imagelist::iterator itor = ret.images.begin(); itor != ret.images.end(); ++itor) {
550  double vx, vy, rx, ry;
551 
552  vx = static_cast<double>(itor->basex) - static_cast<double>(tilewidth_) / 2;
553  vy = static_cast<double>(itor->basey) - static_cast<double>(tilewidth_) / 2;
554 
555  rx = xyrotations[angle].xx * vx + xyrotations[angle].xy * vy;
556  ry = xyrotations[angle].yx * vx + xyrotations[angle].yy * vy;
557 
558  itor->basex = static_cast<int>(rx + tilewidth_ / 2);
559  itor->basey = static_cast<int>(ry + tilewidth_ / 2);
560  }
561 }
562 
563 void terrain_builder::replace_rotate_tokens(std::string& s, int angle, const std::vector<std::string>& replacement)
564 {
565  std::string::size_type pos = 0;
566  while((pos = s.find("@R", pos)) != std::string::npos) {
567  if(pos + 2 >= s.size())
568  return;
569  unsigned i = s[pos + 2] - '0' + angle;
570  if(i >= 6)
571  i -= 6;
572  if(i >= 6) {
573  pos += 2;
574  continue;
575  }
576  const std::string& r = replacement[i];
577  s.replace(pos, 3, r);
578  pos += r.size();
579  }
580 }
581 
582 void terrain_builder::replace_rotate_tokens(rule_image& image, int angle, const std::vector<std::string>& replacement)
583 {
584  for(rule_image_variant& variant : image.variants) {
585  replace_rotate_tokens(variant, angle, replacement);
586  }
587 }
588 
590  rule_imagelist& list, int angle, const std::vector<std::string>& replacement)
591 {
592  for(rule_image& img : list) {
593  replace_rotate_tokens(img, angle, replacement);
594  }
595 }
596 
597 void terrain_builder::replace_rotate_tokens(building_rule& rule, int angle, const std::vector<std::string>& replacement)
598 {
599  for(terrain_constraint& cons : rule.constraints) {
600  // Transforms attributes
601  for(std::string& flag : cons.set_flag) {
602  replace_rotate_tokens(flag, angle, replacement);
603  }
604  for(std::string& flag : cons.no_flag) {
605  replace_rotate_tokens(flag, angle, replacement);
606  }
607  for(std::string& flag : cons.has_flag) {
608  replace_rotate_tokens(flag, angle, replacement);
609  }
610  replace_rotate_tokens(cons.images, angle, replacement);
611  }
612 
613  // replace_rotate_tokens(rule.images, angle, replacement);
614 }
615 
616 void terrain_builder::rotate_rule(building_rule& ret, int angle, const std::vector<std::string>& rot)
617 {
618  if(rot.size() != 6) {
619  ERR_NG << "invalid rotations";
620  return;
621  }
622 
623  for(terrain_constraint& cons : ret.constraints) {
624  rotate(cons, angle);
625  }
626 
627  // Normalize the rotation, so that it starts on a positive location
628  int minx = std::numeric_limits<int>::max();
629  int miny = std::numeric_limits<int>::max();
630 
631  for(const terrain_constraint& cons : ret.constraints) {
632  minx = std::min<int>(cons.loc.x, minx);
633  miny = std::min<int>(2 * cons.loc.y + (cons.loc.x & 1), miny);
634  }
635 
636  if((miny & 1) && (minx & 1) && (minx < 0))
637  miny += 2;
638  if(!(miny & 1) && (minx & 1) && (minx > 0))
639  miny -= 2;
640 
641  for(terrain_constraint& cons : ret.constraints) {
642  legacy_sum_assign(cons.loc, map_location(-minx, -((miny - 1) / 2)));
643  }
644 
645  replace_rotate_tokens(ret, angle, rot);
646 }
647 
649  const std::string& variations,
650  const std::chrono::milliseconds& random_start)
651  : image_string(image_string)
652  , variations(variations)
653  , images()
654  , tods()
655  , has_flag()
656  , random_start(random_start)
657 {
658 }
659 
661  const std::string& variations,
662  const std::string& tod,
663  const std::string& has_flag,
664  const std::chrono::milliseconds& random_start)
665  : image_string(image_string)
666  , variations(variations)
667  , images()
668  , tods()
669  , has_flag()
670  , random_start(random_start)
671 {
672  if(!has_flag.empty()) {
673  this->has_flag = utils::split(has_flag);
674  }
675  if(!tod.empty()) {
676  const std::vector<std::string> tod_list = utils::split(tod);
677  tods.insert(tod_list.begin(), tod_list.end());
678  }
679 }
680 
681 void terrain_builder::add_images_from_config(rule_imagelist& images, const config& cfg, bool global, int dx, int dy)
682 {
683  for(const config& img : cfg.child_range("image")) {
684  int layer = img["layer"].to_int();
685 
686  int basex = tilewidth_ / 2 + dx, basey = tilewidth_ / 2 + dy;
687  if(const config::attribute_value* base_ = img.get("base")) {
688  std::vector<std::string> base = utils::split(*base_);
689  if(base.size() >= 2) {
690  try {
691  basex = std::stoi(base[0]);
692  basey = std::stoi(base[1]);
693  } catch(const std::invalid_argument&) {
694  ERR_NG << "Invalid 'base' value in terrain image builder: " << base[0] << ", " << base[1];
695  }
696  }
697  }
698 
699  int center_x = -1, center_y = -1;
700  if(const config::attribute_value* center_ = img.get("center")) {
701  std::vector<std::string> center = utils::split(*center_);
702  if(center.size() >= 2) {
703  try {
704  center_x = std::stoi(center[0]);
705  center_y = std::stoi(center[1]);
706  } catch(const std::invalid_argument&) {
707  ERR_NG << "Invalid 'center' value in terrain image builder: " << center[0] << ", " << center[1];
708  }
709  }
710  }
711 
712  bool is_water = img["is_water"].to_bool();
713 
714  images.AGGREGATE_EMPLACE(layer, basex - dx, basey - dy, global, center_x, center_y, is_water);
715 
716  // Adds the other variants of the image
717  for(const config& variant : img.child_range("variant")) {
718  const std::string& name = variant["name"];
719  const std::string& variations = img["variations"];
720  const std::string& tod = variant["tod"];
721  const std::string& has_flag = variant["has_flag"];
722 
723  // If an integer is given then assign that, but if a bool is given, then assign -1 if true and 0 if false
724  int random_start = variant["random_start"].to_bool(true) ? variant["random_start"].to_int(-1) : 0;
725 
726  images.back().variants.emplace_back(name, variations, tod, has_flag, std::chrono::milliseconds{random_start});
727  }
728 
729  // Adds the main (default) variant of the image at the end,
730  // (will be used only if previous variants don't match)
731  const std::string& name = img["name"];
732  const std::string& variations = img["variations"];
733 
734  int random_start = img["random_start"].to_bool(true) ? img["random_start"].to_int(-1) : 0;
735 
736  images.back().variants.emplace_back(name, variations, std::chrono::milliseconds{random_start});
737  }
738 }
739 
741  const map_location& loc,
743  const config& global_images)
744 {
745  terrain_constraint* cons = nullptr;
746  for(terrain_constraint& c : constraints) {
747  if(c.loc == loc) {
748  cons = &c;
749  break;
750  }
751  }
752 
753  if(!cons) {
754  // The terrain at the current location did not exist, so create it
755  constraints.AGGREGATE_EMPLACE(loc);
756  cons = &constraints.back();
757  }
758 
759  if(!type.terrain.empty()) {
760  cons->terrain_types_match = type;
761  }
762 
763  int x = loc.x * tilewidth_ * 3 / 4;
764  int y = loc.y * tilewidth_ + (loc.x % 2) * tilewidth_ / 2;
765  add_images_from_config(cons->images, global_images, true, x, y);
766 
767  return *cons;
768 }
769 
771  const map_location& loc,
772  const config& cfg,
773  const config& global_images)
774 
775 {
776  terrain_constraint& constraint = add_constraints(
777  constraints, loc, t_translation::ter_match(cfg["type"].str(), t_translation::WILDCARD), global_images);
778 
779  std::vector<std::string> item_string = utils::square_parenthetical_split(cfg["set_flag"], ',', "[", "]");
780  constraint.set_flag.insert(constraint.set_flag.end(), item_string.begin(), item_string.end());
781 
782  item_string = utils::square_parenthetical_split(cfg["has_flag"], ',', "[", "]");
783  constraint.has_flag.insert(constraint.has_flag.end(), item_string.begin(), item_string.end());
784 
785  item_string = utils::square_parenthetical_split(cfg["no_flag"], ',', "[", "]");
786  constraint.no_flag.insert(constraint.no_flag.end(), item_string.begin(), item_string.end());
787 
788  item_string = utils::square_parenthetical_split(cfg["set_no_flag"], ',', "[", "]");
789  constraint.set_flag.insert(constraint.set_flag.end(), item_string.begin(), item_string.end());
790  constraint.no_flag.insert(constraint.no_flag.end(), item_string.begin(), item_string.end());
791 
792  constraint.no_draw = cfg["no_draw"].to_bool(false);
793 
794  add_images_from_config(constraint.images, cfg, false);
795 }
796 
798  const std::string& mapstring, struct building_rule& br, anchormap& anchors, const config& global_images)
799 {
801 
802  // If there is an empty map leave directly.
803  // Determine after conversion, since a
804  // non-empty string can return an empty map.
805  if(map.data.empty()) {
806  return;
807  }
808 
809  int lineno = (map.get(0, 0) == t_translation::NONE_TERRAIN) ? 1 : 0;
810  int x = lineno;
811  int y = 0;
812  for(int y_off = 0; y_off < map.w; ++y_off) {
813  for(int x_off = x; x_off < map.h; ++x_off) {
814  const t_translation::terrain_code terrain = map.get(y_off, x_off);
815 
816  if(terrain.base == t_translation::TB_DOT) {
817  // Dots are simple placeholders,
818  // which do not represent actual terrains.
819  } else if(terrain.overlay != 0) {
820  anchors.emplace(terrain.overlay, map_location(x, y));
821  } else if(terrain.base == t_translation::TB_STAR) {
822  add_constraints(br.constraints, map_location(x, y), t_translation::STAR, global_images);
823  } else {
824  ERR_NG << "Invalid terrain (" << t_translation::write_terrain_code(terrain) << ") in builder map";
825  assert(false);
826  return;
827  }
828  x += 2;
829  }
830 
831  if(lineno % 2 == 1) {
832  ++y;
833  x = 0;
834  } else {
835  x = 1;
836  }
837  ++lineno;
838  }
839 }
840 
842 {
843  if(load_images(rule)) {
844  rules.insert(rule);
845  }
846 }
847 
848 void terrain_builder::add_rotated_rules(building_ruleset& rules, building_rule& tpl, const std::string& rotations)
849 {
850  if(rotations.empty()) {
851  // Adds the parsed built terrain to the list
852 
853  add_rule(rules, tpl);
854  } else {
855  const std::vector<std::string>& rot = utils::split(rotations, ',');
856 
857  for(std::size_t angle = 0; angle < rot.size(); ++angle) {
858  /* Only 5% of the rules have valid images, so most of
859  them will be discarded. If the ratio was higher,
860  it would be more efficient to insert a copy of the
861  template rule into the ruleset, modify it in place,
862  and remove it if invalid. But since the ratio is so
863  low, the speedup is not worth the extra multiset
864  manipulations. */
865 
866  if(rot.at(angle) == "skip") {
867  continue;
868  }
869 
870  building_rule rule = tpl;
871  rotate_rule(rule, angle, rot);
872  add_rule(rules, rule);
873  }
874  }
875 }
876 
877 void terrain_builder::parse_config(const config& cfg, bool local)
878 {
879  return parse_config(game_config_view::wrap(cfg), local);
880 }
881 
883 {
884  log_scope("terrain_builder::parse_config");
885  int n = 0;
886 
887  // Parses the list of building rules (BRs)
888  for(const config& br : cfg.child_range("terrain_graphics")) {
889  building_rule pbr; // Parsed Building rule
890  pbr.local = local;
891 
892  // add_images_from_config(pbr.images, **br);
893 
894  pbr.location_constraints = map_location(br["x"].to_int() - 1, br["y"].to_int() - 1);
895 
896  pbr.modulo_constraints = map_location(br["mod_x"].to_int(), br["mod_y"].to_int());
897 
898  pbr.probability = br["probability"].to_int(100);
899 
900  // Mapping anchor indices to anchor locations.
901  anchormap anchors;
902 
903  // Parse the map= , if there is one (and fill the anchors list)
904  parse_mapstring(br["map"], pbr, anchors, br);
905 
906  // Parses the terrain constraints (TCs)
907  for(const config& tc : br.child_range("tile")) {
908  // Adds the terrain constraint to the current built terrain's list
909  // of terrain constraints, if it does not exist.
911  if(const config::attribute_value* v = tc.get("x")) {
912  loc.x = v->to_int();
913  }
914  if(const config::attribute_value* v = tc.get("y")) {
915  loc.y = v->to_int();
916  }
917  if(loc.valid()) {
918  add_constraints(pbr.constraints, loc, tc, br);
919  }
920  if(const config::attribute_value* v = tc.get("pos")) {
921  int pos = v->to_int();
922  if(anchors.find(pos) == anchors.end()) {
923  WRN_NG << "Invalid anchor!";
924  continue;
925  }
926 
927  std::pair<anchormap::const_iterator, anchormap::const_iterator> range = anchors.equal_range(pos);
928 
929  for(; range.first != range.second; ++range.first) {
930  loc = range.first->second;
931  add_constraints(pbr.constraints, loc, tc, br);
932  }
933  }
934  }
935 
936  const std::vector<std::string> global_set_flag = utils::split(br["set_flag"]);
937  const std::vector<std::string> global_no_flag = utils::split(br["no_flag"]);
938  const std::vector<std::string> global_has_flag = utils::split(br["has_flag"]);
939  const std::vector<std::string> global_set_no_flag = utils::split(br["set_no_flag"]);
940 
941  for(terrain_constraint& constraint : pbr.constraints) {
942  constraint.set_flag.insert(constraint.set_flag.end(), global_set_flag.begin(), global_set_flag.end());
943  constraint.no_flag.insert(constraint.no_flag.end(), global_no_flag.begin(), global_no_flag.end());
944  constraint.has_flag.insert(constraint.has_flag.end(), global_has_flag.begin(), global_has_flag.end());
945  constraint.set_flag.insert(constraint.set_flag.end(), global_set_no_flag.begin(), global_set_no_flag.end());
946  constraint.no_flag.insert(constraint.no_flag.end(), global_set_no_flag.begin(), global_set_no_flag.end());
947  }
948 
949  // Handles rotations
950  const std::string& rotations = br["rotations"];
951 
952  pbr.precedence = br["precedence"].to_int();
953 
954  add_rotated_rules(building_rules_, pbr, rotations);
955 
956  n++;
957  if(n % 10 == 0) {
959  }
960  }
961 
962 // Debug output for the terrain rules
963 #if 0
964  PLAIN_LOG << "Built terrain rules: ";
965 
966  building_ruleset::const_iterator rule;
967  for(rule = building_rules_.begin(); rule != building_rules_.end(); ++rule) {
968  PLAIN_LOG << ">> New rule: image_background = "
969  << "\n>> Location " << rule->second.location_constraints
970  << "\n>> Probability " << rule->second.probability
971 
972  for(constraint_set::const_iterator constraint = rule->second.constraints.begin();
973  constraint != rule->second.constraints.end(); ++constraint) {
974 
975  PLAIN_LOG << ">>>> New constraint: location = (" << constraint->second.loc
976  << "), terrain types = '" << t_translation::write_list(constraint->second.terrain_types_match.terrain) << "'";
977 
978  std::vector<std::string>::const_iterator flag;
979 
980  for(flag = constraint->second.set_flag.begin(); flag != constraint->second.set_flag.end(); ++flag) {
981  PLAIN_LOG << ">>>>>> Set_flag: " << *flag;
982  }
983 
984  for(flag = constraint->second.no_flag.begin(); flag != constraint->second.no_flag.end(); ++flag) {
985  PLAIN_LOG << ">>>>>> No_flag: " << *flag;
986  }
987  }
988 
989  }
990 #endif
991 }
992 
993 void terrain_builder::add_off_map_rule(const std::string& image)
994 {
995  // Build a config object
996  config cfg;
997 
998  config& item = cfg.add_child("terrain_graphics");
999 
1000  config& tile = item.add_child("tile");
1001  tile["x"] = 0;
1002  tile["y"] = 0;
1004 
1005  config& tile_image = tile.add_child("image");
1006  tile_image["layer"] = -1000;
1007  tile_image["name"] = image;
1008 
1009  item["probability"] = 100;
1010  item["no_flag"] = "base";
1011  item["set_flag"] = "base";
1012 
1013  // Parse the object
1015 }
1016 
1018  const map_location& loc,
1019  const terrain_constraint* type_checked) const
1020 {
1021  // Don't match if the location isn't a multiple of mod_x and mod_y
1022  if(rule.modulo_constraints.x > 0 && (loc.x % rule.modulo_constraints.x != 0)) {
1023  return false;
1024  }
1025  if(rule.modulo_constraints.y > 0 && (loc.y % rule.modulo_constraints.y != 0)) {
1026  return false;
1027  }
1028 
1029  if(rule.location_constraints.valid() && rule.location_constraints != loc) {
1030  return false;
1031  }
1032 
1033  if(rule.probability != 100) {
1034  unsigned int random = get_noise(loc, rule.get_hash()) % 100;
1035  if(random > static_cast<unsigned int>(rule.probability)) {
1036  return false;
1037  }
1038  }
1039 
1040  for(const terrain_constraint& cons : rule.constraints) {
1041  // Translated location
1042  const map_location tloc = legacy_sum(loc, cons.loc);
1043 
1044  if(!tile_map_.on_map(tloc)) {
1045  return false;
1046  }
1047 
1048  // std::cout << "testing..." << builder_letter(map().get_terrain(tloc))
1049 
1050  // check if terrain matches except if we already know that it does
1051  if(&cons != type_checked && !terrain_matches(map().get_terrain(tloc), cons.terrain_types_match)) {
1052  return false;
1053  }
1054 
1055  const std::set<std::string>& flags = tile_map_[tloc].flags;
1056 
1057  for(const std::string& s : cons.no_flag) {
1058  // If a flag listed in "no_flag" is present, the rule does not match
1059  if(flags.find(s) != flags.end()) {
1060  return false;
1061  }
1062  }
1063  for(const std::string& s : cons.has_flag) {
1064  // If a flag listed in "has_flag" is not present, this rule does not match
1065  if(flags.find(s) == flags.end()) {
1066  return false;
1067  }
1068  }
1069  }
1070 
1071  return true;
1072 }
1073 
1075 {
1076  unsigned int rand_seed = get_noise(loc, rule.get_hash());
1077 
1078  for(const terrain_constraint& constraint : rule.constraints) {
1079  const map_location tloc = legacy_sum(loc, constraint.loc);
1080  if(!tile_map_.on_map(tloc)) {
1081  return;
1082  }
1083 
1084  tile& btile = tile_map_[tloc];
1085 
1086  if(!constraint.no_draw) {
1087  for(const rule_image& img : constraint.images) {
1088  btile.images.AGGREGATE_EMPLACE(&img, rand_seed);
1089  }
1090  }
1091 
1092  // Sets flags
1093  for(const std::string& flag : constraint.set_flag) {
1094  btile.flags.insert(flag);
1095  }
1096  }
1097 }
1098 
1099 // copied from text_surface::hash()
1100 // but keep it separated because the needs are different
1101 // and changing it will modify the map random variations
1102 static unsigned int hash_str(const std::string& str)
1103 {
1104  unsigned int h = 0;
1105  for(std::string::const_iterator it = str.begin(), it_end = str.end(); it != it_end; ++it)
1106  h = ((h << 9) | (h >> (sizeof(int) * 8 - 9))) ^ (*it);
1107  return h;
1108 }
1109 
1111 {
1112  if(hash_ != DUMMY_HASH)
1113  return hash_;
1114 
1115  for(const terrain_constraint& constraint : constraints) {
1116  for(const rule_image& ri : constraint.images) {
1117  for(const rule_image_variant& variant : ri.variants) {
1118  // we will often hash the same string, but that seems fast enough
1119  hash_ += hash_str(variant.image_string);
1120  }
1121  }
1122  }
1123 
1124  // don't use the reserved dummy hash
1125  if(hash_ == DUMMY_HASH)
1126  hash_ = 105533; // just a random big prime number
1127 
1128  return hash_;
1129 }
1130 
1132 {
1133  log_scope("terrain_builder::build_terrains");
1134 
1135  // Builds the terrain_by_type_ cache
1136  for(int x = -2; x <= map().w(); ++x) {
1137  for(int y = -2; y <= map().h(); ++y) {
1138  const map_location loc(x, y);
1140 
1141  terrain_by_type_[t].push_back(loc);
1142 
1143  // Flag all hexes according to whether they're on the border or not,
1144  // to make it easier for WML to draw the borders
1145  if(draw_border_&& !map().on_board(loc)) {
1146  tile_map_[loc].flags.insert("_border");
1147  } else {
1148  tile_map_[loc].flags.insert("_board");
1149  }
1150  }
1151  }
1152 
1153  for(const building_rule& rule : building_rules_) {
1154  // Find the constraint that contains the less terrain of all terrain rules.
1155  // We will keep a track of the matching terrains of this constraint
1156  // and later try to apply the rule only on them
1157  std::size_t min_size = std::numeric_limits<int>::max();
1158  t_translation::ter_list min_types = t_translation::ter_list(); // <-- This must be explicitly initialized, just
1159  // as min_constraint is, at start of loop, or we
1160  // get a null pointer dereference when we go
1161  // through on later times.
1162  const terrain_constraint* min_constraint = nullptr;
1163 
1164  for(const terrain_constraint& constraint : rule.constraints) {
1165  const t_translation::ter_match& match = constraint.terrain_types_match;
1166  t_translation::ter_list matching_types;
1167  std::size_t constraint_size = 0;
1168 
1169  for(terrain_by_type_map::iterator type_it = terrain_by_type_.begin(); type_it != terrain_by_type_.end();
1170  ++type_it) {
1171  const t_translation::terrain_code t = type_it->first;
1172  if(terrain_matches(t, match)) {
1173  const std::size_t match_size = type_it->second.size();
1174  constraint_size += match_size;
1175  if(constraint_size >= min_size) {
1176  break; // not a minimum, bail out
1177  }
1178  matching_types.push_back(t);
1179  }
1180  }
1181 
1182  if(constraint_size < min_size) {
1183  min_size = constraint_size;
1184  min_types = matching_types;
1185  min_constraint = &constraint;
1186  if(min_size == 0) {
1187  // a constraint is never matched on this map
1188  // we break with a empty type list
1189  break;
1190  }
1191  }
1192  }
1193 
1194  assert(min_constraint != nullptr);
1195 
1196  // NOTE: if min_types is not empty, we have found a valid min_constraint;
1197  for(t_translation::ter_list::const_iterator t = min_types.begin(); t != min_types.end(); ++t) {
1198  const std::vector<map_location>* locations = &terrain_by_type_[*t];
1199 
1200  for(std::vector<map_location>::const_iterator itor = locations->begin(); itor != locations->end(); ++itor) {
1201  const map_location loc = legacy_difference(*itor, min_constraint->loc);
1202 
1203  if(rule_matches(rule, loc, min_constraint)) {
1204  apply_rule(rule, loc);
1205  }
1206  }
1207  }
1208  }
1209 }
1210 
1212 {
1213  if(tile_map_.on_map(loc))
1214  return &(tile_map_[loc]);
1215  return nullptr;
1216 }
map_location loc
Definition: move.cpp:172
double t
Definition: astarsearch.cpp:63
#define WRN_NG
Definition: builder.cpp:34
static map_location legacy_negation(const map_location &me)
These legacy map_location functions moved here from map_location.
Definition: builder.cpp:56
static lg::log_domain log_engine("engine")
#define ERR_NG
Definition: builder.cpp:33
static std::vector< std::string > get_variations(const std::string &base, const std::string &variations)
Definition: builder.cpp:397
static unsigned int hash_str(const std::string &str)
Definition: builder.cpp:1102
static unsigned int get_noise(const map_location &loc, unsigned int index)
Definition: builder.cpp:178
static map_location & legacy_sum_assign(map_location &me, const map_location &a)
Definition: builder.cpp:61
static map_location legacy_sum(const map_location &me, const map_location &a)
Definition: builder.cpp:74
static bool image_exists(const std::string &name)
Definition: builder.cpp:384
static map_location legacy_difference(const map_location &me, const map_location &a)
Definition: builder.cpp:81
Definitions for the terrain builder.
const T & get_frame(std::size_t n) const
void start_animation(const std::chrono::milliseconds &start_time, bool cycles=false)
Starts an animation cycle.
void add_frame(const std::chrono::milliseconds &duration, const T &value, bool force_change=false)
Adds a frame to an animation.
void update_last_draw_time(double acceleration=0)
bool need_update() const
std::size_t get_frames_count() const
std::chrono::milliseconds get_animation_duration() const
Variant for storing WML attributes.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:172
child_itors child_range(config_key_type key)
Definition: config.cpp:272
config & add_child(config_key_type key)
Definition: config.cpp:440
A class grating read only view to a vector of config objects, viewed as one config with all children ...
static game_config_view wrap(const config &cfg)
config_array_view child_range(config_key_type key) const
terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition: map.cpp:302
int w() const
Effective map width.
Definition: map.hpp:50
int h() const
Effective map height.
Definition: map.hpp:53
bool empty() const
Tell if the map is of 0 size.
Definition: map.hpp:65
Encapsulates the map of the game.
Definition: map.hpp:172
const terrain_type & get_terrain_info(const t_translation::terrain_code &terrain) const
Definition: map.cpp:98
static void progress(loading_stage stage=loading_stage::none)
Report what is being loaded to the loading screen.
Generic locator abstracting the location of an image.
Definition: picture.hpp:59
static prefs & get()
tile & operator[](const map_location &loc)
Returns a reference to the tile which is at the position pointed by loc.
Definition: builder.cpp:219
bool on_map(const map_location &loc) const
Tests if a location is on the map.
Definition: builder.cpp:210
void reset()
Resets the whole tile map.
Definition: builder.cpp:195
tilemap(int x, int y)
Constructs a tilemap of dimensions x * y.
Definition: builder.cpp:187
void reload(int x, int y)
Rebuilds the map to a new set of dimensions.
Definition: builder.cpp:201
void parse_mapstring(const std::string &mapstring, struct building_rule &br, anchormap &anchors, const config &global_images)
Parses a map string (the map= element of a [terrain_graphics] rule, and adds constraints from this ma...
Definition: builder.cpp:797
std::vector< animated< image::locator > > imagelist
A shorthand typedef for a list of animated image locators, the base data type returned by the get_ter...
Definition: builder.hpp:74
std::multiset< building_rule > building_ruleset
A set of building rules.
Definition: builder.hpp:468
void parse_config(const config &cfg, bool local=true)
Parses a configuration object containing [terrain_graphics] rules, and fills the building_rules_ memb...
Definition: builder.cpp:877
tile * get_tile(const map_location &loc)
Definition: builder.cpp:1211
void add_rule(building_ruleset &rules, building_rule &rule)
Adds a rule to a ruleset.
Definition: builder.cpp:841
terrain_constraint & add_constraints(constraint_set &constraints, const map_location &loc, const t_translation::ter_match &type, const config &global_images)
Creates a rule constraint object which matches a given list of terrains, and adds it to the list of c...
Definition: builder.cpp:740
void rotate_rule(building_rule &rule, int angle, const std::vector< std::string > &angle_name)
Rotates a template rule to a given angle.
Definition: builder.cpp:616
bool draw_border_
Whether the map border should be drawn.
Definition: builder.hpp:797
void add_off_map_rule(const std::string &image)
Adds a builder rule for the _off^_usr tile, this tile only has 1 image.
Definition: builder.cpp:993
void build_terrains()
Calculates the list of terrains, and fills the tile_map_ member, from the gamemap and the building_ru...
Definition: builder.cpp:1131
void add_images_from_config(rule_imagelist &images, const config &cfg, bool global, int dx=0, int dy=0)
Parses a "config" object, which should contains [image] children, and adds the corresponding parsed r...
Definition: builder.cpp:681
void rebuild_terrain(const map_location &loc)
Performs a "quick-rebuild" of the terrain in a given location.
Definition: builder.cpp:347
bool update_animation(const map_location &loc)
Updates the animation at a given tile.
Definition: builder.cpp:323
static building_ruleset building_rules_
Parsed terrain rules.
Definition: builder.hpp:800
const gamemap & map() const
Definition: builder.hpp:99
static const unsigned int DUMMY_HASH
Definition: builder.hpp:69
bool rule_matches(const building_rule &rule, const map_location &loc, const terrain_constraint *type_checked) const
Checks whether a rule matches a given location in the map.
Definition: builder.cpp:1017
void change_map(const gamemap *m)
Definition: builder.cpp:295
static void set_terrain_rules_cfg(const game_config_view &cfg)
Set the config where we will parse the global terrain rules.
Definition: builder.cpp:279
std::vector< terrain_constraint > constraint_set
The list of constraints attached to a terrain_graphics WML rule.
Definition: builder.hpp:356
const int tilewidth_
The tile width used when using basex and basey.
Definition: builder.hpp:351
const gamemap * map_
A pointer to the gamemap class used in the current level.
Definition: builder.hpp:777
bool terrain_matches(const t_translation::terrain_code &tcode, const t_translation::ter_list &terrains) const
Checks whether a terrain code matches a given list of terrain codes.
Definition: builder.hpp:727
const imagelist * get_terrain_at(const map_location &loc, const std::string &tod, TERRAIN_TYPE const terrain_type)
Returns a vector of strings representing the images to load & blit together to get the built content ...
Definition: builder.cpp:301
bool load_images(building_rule &rule)
Load images and tests for validity of a rule.
Definition: builder.cpp:423
tilemap tile_map_
The tile_map_ for the current level, which is filled by the build_terrains_ method to contain "tiles"...
Definition: builder.hpp:784
void apply_rule(const building_rule &rule, const map_location &loc)
Applies a rule at a given location: applies the result of a matching rule at a given location: attach...
Definition: builder.cpp:1074
void flush_local_rules()
Definition: builder.cpp:268
void rotate(terrain_constraint &constraint, int angle)
"Rotates" a constraint from a rule.
Definition: builder.cpp:483
void rebuild_all()
Performs a complete rebuild of the list of terrain graphics attached to a map.
Definition: builder.cpp:377
std::multimap< int, map_location > anchormap
Definition: builder.hpp:654
void add_rotated_rules(building_ruleset &rules, building_rule &tpl, const std::string &rotations)
Adds a set of rules to a ruleset, from a template rule which spans 6 rotations (or less if some of th...
Definition: builder.cpp:848
TERRAIN_TYPE
Used as a parameter for the get_terrain_at function.
Definition: builder.hpp:51
@ BACKGROUND
Represents terrains which are to be drawn behind unit sprites.
Definition: builder.hpp:52
std::vector< rule_image > rule_imagelist
A shorthand notation for a vector of rule_images.
Definition: builder.hpp:258
terrain_by_type_map terrain_by_type_
A map representing all locations whose terrain is of a given type.
Definition: builder.hpp:794
static const game_config_view * rules_cfg_
Config used to parse global terrain rules.
Definition: builder.hpp:803
void parse_global_config(const game_config_view &cfg)
Definition: builder.hpp:701
void reload_map()
Updates internals that cache map size.
Definition: builder.cpp:288
void replace_rotate_tokens(std::string &s, int angle, const std::vector< std::string > &replacement)
Replaces, in a given string, rotation tokens with their values.
Definition: builder.cpp:563
void rebuild_cache_all()
Definition: builder.cpp:259
terrain_builder(const config &level, const gamemap *map, const std::string &offmap_image, bool draw_border)
Constructor for the terrain_builder class.
Definition: builder.cpp:233
const std::string & minimap_image() const
Definition: terrain.hpp:45
const std::string & minimap_image_overlay() const
Definition: terrain.hpp:46
void swap(config &lhs, config &rhs)
Implement non-member swap function for std::swap (calls config::swap).
Definition: config.cpp:1343
std::size_t i
Definition: function.cpp:1029
int w
Standard logging facilities (interface).
#define PLAIN_LOG
Definition: log.hpp:297
#define log_scope(description)
Definition: log.hpp:276
Game configuration data as global variables.
Definition: build_info.cpp:61
unsigned int tile_size
Definition: game_config.cpp:55
Functions to load and save images from/to disk.
bool is_empty_hex(const locator &i_locator)
Checks if an image is empty after hex masking.
Definition: picture.cpp:797
bool precached_file_exists(const std::string &file)
Definition: picture.cpp:874
bool exists(const image::locator &i_locator)
Returns true if the given image actually exists, without loading it.
Definition: picture.cpp:818
void precache_file_existence(const std::string &subdir)
Precache the existence of files in a binary path subdirectory (e.g.
Definition: picture.cpp:867
static const std::string br
Definition: markup.hpp:33
std::string img(const std::string &src, const std::string &align, const bool floating)
Definition: markup.cpp:29
const ter_layer TB_STAR
const ter_layer WILDCARD
Definition: translation.hpp:39
const terrain_code STAR
std::vector< terrain_code > ter_list
Definition: translation.hpp:77
ter_map read_builder_map(const std::string &str)
Reads a builder map.
const ter_layer TB_DOT
std::string write_terrain_code(const terrain_code &tcode)
Writes a single terrain code to a string.
const terrain_code OFF_MAP_USER
std::string write_list(const ter_list &list)
Writes a list of terrains to a string, only writes the new format.
const terrain_code NONE_TERRAIN
Definition: translation.hpp:58
std::size_t index(std::string_view str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:70
int stoi(std::string_view str)
Same interface as std::stoi and meant as a drop in replacement, except:
Definition: charconv.hpp:154
std::vector< std::string > square_parenthetical_split(const std::string &val, const char separator, const std::string &left, const std::string &right, const int flags)
Similar to parenthetical_split, but also expands embedded square brackets.
std::vector< std::string > split(const config_attribute_value &val)
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
std::string filename
Filename.
Encapsulates the map of the game.
Definition: location.hpp:45
bool valid() const
Definition: location.hpp:110
This structure can be used for matching terrain strings.
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:49
The in-memory representation of a [terrain_graphics] WML rule.
Definition: builder.hpp:362
int precedence
Ordering relation between the rules.
Definition: builder.hpp:392
map_location modulo_constraints
Used to constrain locations to ones with coordinates that are multiples of the "mod_x" and "mod_y" pa...
Definition: builder.hpp:380
map_location location_constraints
The location on which this map may match.
Definition: builder.hpp:373
int probability
The probability of this rule to match, when all conditions are met.
Definition: builder.hpp:387
unsigned int get_hash() const
Definition: builder.cpp:1110
constraint_set constraints
The set of [tile] constraints of this rule.
Definition: builder.hpp:366
std::vector< std::string > has_flag
Definition: builder.hpp:212
rule_image_variant(const std::string &image_string, const std::string &variations, const std::chrono::milliseconds &random_start=std::chrono::milliseconds{-1})
Constructor for the normal default case.
Definition: builder.cpp:648
std::chrono::milliseconds random_start
Specify the allowed amount of random shift (in milliseconds) applied to the animation start time,...
Definition: builder.hpp:216
std::vector< animated< image::locator > > images
An animated image locator built according to the image string.
Definition: builder.hpp:207
std::set< std::string > tods
The Time of Day associated to this variant (if any)
Definition: builder.hpp:210
std::string image_string
A string representing either the filename for an image, or a list of images, with an optional timing ...
Definition: builder.hpp:196
std::string variations
A semi-solon separated list of string used to replace.
Definition: builder.hpp:201
Each terrain_graphics rule is associated a set of images, which are applied on the terrain if the rul...
Definition: builder.hpp:227
bool global_image
Set to true if the image was defined as a child of the [terrain_graphics] tag, set to false if it was...
Definition: builder.hpp:243
std::vector< rule_image_variant > variants
A list of variants for this image.
Definition: builder.hpp:252
int center_x
The position where the center of the image base should be.
Definition: builder.hpp:247
The in-memory representation of a [tile] WML rule inside of a [terrain_graphics] WML rule.
Definition: builder.hpp:265
t_translation::ter_match terrain_types_match
Definition: builder.hpp:267
std::vector< std::string > set_flag
Definition: builder.hpp:268
std::vector< std::string > has_flag
Definition: builder.hpp:270
bool no_draw
Whether to actually draw the images onto this hex or not.
Definition: builder.hpp:273
std::vector< std::string > no_flag
Definition: builder.hpp:269
Represent a rule_image applied with a random seed.
Definition: builder.hpp:306
Represents a tile of the game map, with all associated builder-specific parameters: flags,...
Definition: builder.hpp:285
std::string last_tod
The time-of-day to which the image caches correspond.
Definition: builder.hpp:338
std::vector< rule_image_rand > images
The list of rule_images and random seeds associated to this tile.
Definition: builder.hpp:323
std::vector< log_details > logs
Definition: builder.hpp:288
void clear()
Clears all data in this tile, and resets the cache.
Definition: builder.cpp:168
void rebuild_cache(const std::string &tod, logs *log=nullptr)
Rebuilds the whole image cache, for a given time-of-day.
Definition: builder.cpp:92
imagelist images_background
The list of images which are behind the unit sprites, attached to this tile.
Definition: builder.hpp:334
imagelist images_foreground
The list of images which are in front of the unit sprites, attached to this tile.
Definition: builder.hpp:329
std::set< std::string > flags
The list of flags present in this tile.
Definition: builder.hpp:302
mock_char c
static map_location::direction n
static map_location::direction s
#define h
#define b