The Battle for Wesnoth  1.19.25+dev
builder.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2004 - 2025
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().apply_time_offset(std::chrono::milliseconds{ri.rand} % img_list.back().get_animation_duration());
151  } else if(variant.random_start > 0ms) {
152  img_list.back().apply_time_offset(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<std::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  building_rules_.clear();
283 }
284 
286 {
287  tile_map_.reload(map().w(), map().h());
288  terrain_by_type_.clear();
289  build_terrains();
290 }
291 
293 {
294  map_ = m;
295  reload_map();
296 }
297 
299  const map_location& loc, const std::string& tod, const TERRAIN_TYPE terrain_type)
300 {
301  if(!tile_map_.on_map(loc))
302  return nullptr;
303 
304  tile& tile_at = tile_map_[loc];
305 
306  if(tod != tile_at.last_tod) {
307  tile_at.rebuild_cache(tod);
308  tile_at.last_tod = tod;
309  }
310 
311  const imagelist& img_list = (terrain_type == BACKGROUND) ? tile_at.images_background : tile_at.images_foreground;
312 
313  if(!img_list.empty()) {
314  return &img_list;
315  }
316 
317  return nullptr;
318 }
319 
321 {
322  if(!tile_map_.on_map(loc))
323  return false;
324 
325  bool changed = false;
326 
327  tile& btile = tile_map_[loc];
328 
330  if(a.need_update()) {
331  changed = true;
333  }
334  }
336  if(a.need_update()) {
337  changed = true;
339  }
340  }
341 
342  return changed;
343 }
344 
345 /** @todo TODO: rename this function */
347 {
348  if(tile_map_.on_map(loc)) {
349  tile& btile = tile_map_[loc];
350  // btile.images.clear();
351  btile.images_foreground.clear();
352  btile.images_background.clear();
353  const std::string filename = map().get_terrain_info(loc).minimap_image();
354 
355  if(!filename.empty()) {
356  animated<image::locator> img_loc;
357  img_loc.add_frame(100ms, image::locator("terrain/" + filename + ".png"));
358  img_loc.start_animation(0ms, true);
359  btile.images_background.push_back(img_loc);
360  }
361 
362  // Combine base and overlay image if necessary
363  if(map().get_terrain_info(loc).is_combined()) {
364  const std::string filename_ovl = map().get_terrain_info(loc).minimap_image_overlay();
365 
366  if(!filename_ovl.empty()) {
367  animated<image::locator> img_loc_ovl;
368  img_loc_ovl.add_frame(100ms, image::locator("terrain/" + filename_ovl + ".png"));
369  img_loc_ovl.start_animation(0ms, true);
370  btile.images_background.push_back(img_loc_ovl);
371  }
372  }
373  }
374 }
375 
377 {
378  tile_map_.reset();
379  terrain_by_type_.clear();
380  build_terrains();
381 }
382 
383 static bool image_exists(const std::string& name)
384 {
385  bool precached = name.find("..") == std::string::npos;
386 
387  if(precached && image::precached_file_exists(name)) {
388  return true;
389  } else if(image::exists(name)) {
390  return true;
391  }
392 
393  return false;
394 }
395 
396 static std::vector<std::string> get_variations(const std::string& base, const std::string& variations)
397 {
398  /** @todo optimize this function */
399  std::vector<std::string> res;
400  if(variations.empty()) {
401  res.push_back(base);
402  return res;
403  }
404  std::string::size_type pos = base.find("@V", 0);
405  if(pos == std::string::npos) {
406  res.push_back(base);
407  return res;
408  }
409  std::vector<std::string> vars = utils::split(variations, ';', 0);
410 
411  for(const std::string& v : vars) {
412  res.push_back(base);
413  pos = 0;
414  while((pos = res.back().find("@V", pos)) != std::string::npos) {
415  res.back().replace(pos, 2, v);
416  pos += v.size();
417  }
418  }
419  return res;
420 }
421 
423 {
424  // If the rule has no constraints, it is invalid
425  if(rule.constraints.empty())
426  return false;
427 
428  // Parse images and animations data
429  // If one is not valid, return false.
430  for(terrain_constraint& constraint : rule.constraints) {
431  for(rule_image& ri : constraint.images) {
432  for(rule_image_variant& variant : ri.variants) {
433  std::vector<std::string> var_strings = get_variations(variant.image_string, variant.variations);
434  for(const std::string& var : var_strings) {
435  /** @todo improve this, 99% of terrains are not animated. */
436  std::vector<std::string> frames = utils::square_parenthetical_split(var, ',');
438 
439  for(const std::string& frame : frames) {
440  const std::vector<std::string> items = utils::split(frame, ':');
441  const std::string& str = items.front();
442 
443  const std::size_t tilde = str.find('~');
444  bool has_tilde = tilde != std::string::npos;
445  const std::string filename = "terrain/" + (has_tilde ? str.substr(0, tilde) : str);
446 
447  if(!image_exists(filename)) {
448  continue; // ignore missing frames
449  }
450 
451  const std::string modif = (has_tilde ? str.substr(tilde + 1) : "");
452 
453  auto time = 100ms;
454  if(items.size() > 1) {
455  try {
456  time = std::chrono::milliseconds{std::stoi(items.back())};
457  } catch(const std::invalid_argument&) {
458  ERR_NG << "Invalid 'time' value in terrain image builder: " << items.back();
459  }
460  }
461  if(ri.global_image) {
462  res.add_frame(time, image::locator(filename, constraint.loc, ri.center_x, ri.center_y, modif));
463  } else {
464  res.add_frame(time, image::locator(filename, modif));
465  }
466  }
467  if(res.get_frames_count() == 0)
468  break; // no valid images, don't register it
469 
470  res.start_animation(0ms, true);
471  variant.images.push_back(std::move(res));
472  }
473  if(variant.images.empty())
474  return false; // no valid images, rule is invalid
475  }
476  }
477  }
478 
479  return true;
480 }
481 
483 {
484  static const struct
485  {
486  int ii;
487  int ij;
488  int ji;
489  int jj;
490  } 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}};
491 
492  // The following array of matrices is intended to rotate the (x,y)
493  // coordinates of a point in a wesnoth hex (and wesnoth hexes are not
494  // regular hexes :) ).
495  // The base matrix for a 1-step rotation with the wesnoth tile shape
496  // is:
497  //
498  // r = s^-1 * t * s
499  //
500  // with s = [[ 1 0 ]
501  // [ 0 -sqrt(3)/2 ]]
502  //
503  // and t = [[ -1/2 sqrt(3)/2 ]
504  // [ -sqrt(3)/2 1/2 ]]
505  //
506  // With t being the rotation matrix (pi/3 rotation), and s a matrix
507  // that transforms the coordinates of the wesnoth hex to make them
508  // those of a regular hex.
509  //
510  // (demonstration left as an exercise for the reader)
511  //
512  // So we have
513  //
514  // r = [[ 1/2 -3/4 ]
515  // [ 1 1/2 ]]
516  //
517  // And the following array contains I(2), r, r^2, r^3, r^4, r^5
518  // (with r^3 == -I(2)), which are the successive rotations.
519  static const struct
520  {
521  double xx;
522  double xy;
523  double yx;
524  double yy;
525  } xyrotations[6] {
526  { 1., 0., 0., 1. },
527  { 1./2. , -3./4., 1., 1./2. },
528  { -1./2., -3./4., 1, -1./2.},
529  { -1. , 0., 0., -1. },
530  { -1./2., 3./4., -1., -1./2.},
531  { 1./2. , 3./4., -1., 1./2. },
532  };
533 
534  assert(angle >= 0);
535 
536  angle %= 6;
537 
538  // Vector i is going from n to s, vector j is going from ne to sw.
539  int vi = ret.loc.y - ret.loc.x / 2;
540  int vj = ret.loc.x;
541 
542  int ri = rotations[angle].ii * vi + rotations[angle].ij * vj;
543  int rj = rotations[angle].ji * vi + rotations[angle].jj * vj;
544 
545  ret.loc.x = rj;
546  ret.loc.y = ri + (rj >= 0 ? rj / 2 : (rj - 1) / 2);
547 
548  for(rule_imagelist::iterator itor = ret.images.begin(); itor != ret.images.end(); ++itor) {
549  double vx, vy, rx, ry;
550 
551  vx = static_cast<double>(itor->basex) - static_cast<double>(tilewidth_) / 2;
552  vy = static_cast<double>(itor->basey) - static_cast<double>(tilewidth_) / 2;
553 
554  rx = xyrotations[angle].xx * vx + xyrotations[angle].xy * vy;
555  ry = xyrotations[angle].yx * vx + xyrotations[angle].yy * vy;
556 
557  itor->basex = static_cast<int>(rx + tilewidth_ / 2);
558  itor->basey = static_cast<int>(ry + tilewidth_ / 2);
559  }
560 }
561 
562 void terrain_builder::replace_rotate_tokens(std::string& s, int angle, const std::vector<std::string>& replacement)
563 {
564  std::string::size_type pos = 0;
565  while((pos = s.find("@R", pos)) != std::string::npos) {
566  if(pos + 2 >= s.size())
567  return;
568  unsigned i = s[pos + 2] - '0' + angle;
569  if(i >= 6)
570  i -= 6;
571  if(i >= 6) {
572  pos += 2;
573  continue;
574  }
575  const std::string& r = replacement[i];
576  s.replace(pos, 3, r);
577  pos += r.size();
578  }
579 }
580 
581 void terrain_builder::replace_rotate_tokens(rule_image& image, int angle, const std::vector<std::string>& replacement)
582 {
583  for(rule_image_variant& variant : image.variants) {
584  replace_rotate_tokens(variant, angle, replacement);
585  }
586 }
587 
589  rule_imagelist& list, int angle, const std::vector<std::string>& replacement)
590 {
591  for(rule_image& img : list) {
592  replace_rotate_tokens(img, angle, replacement);
593  }
594 }
595 
596 void terrain_builder::replace_rotate_tokens(building_rule& rule, int angle, const std::vector<std::string>& replacement)
597 {
598  for(terrain_constraint& cons : rule.constraints) {
599  // Transforms attributes
600  for(std::string& flag : cons.set_flag) {
601  replace_rotate_tokens(flag, angle, replacement);
602  }
603  for(std::string& flag : cons.no_flag) {
604  replace_rotate_tokens(flag, angle, replacement);
605  }
606  for(std::string& flag : cons.has_flag) {
607  replace_rotate_tokens(flag, angle, replacement);
608  }
609  replace_rotate_tokens(cons.images, angle, replacement);
610  }
611 
612  // replace_rotate_tokens(rule.images, angle, replacement);
613 }
614 
615 void terrain_builder::rotate_rule(building_rule& ret, int angle, const std::vector<std::string>& rot)
616 {
617  if(rot.size() != 6) {
618  ERR_NG << "invalid rotations";
619  return;
620  }
621 
622  for(terrain_constraint& cons : ret.constraints) {
623  rotate(cons, angle);
624  }
625 
626  // Normalize the rotation, so that it starts on a positive location
627  int minx = std::numeric_limits<int>::max();
628  int miny = std::numeric_limits<int>::max();
629 
630  for(const terrain_constraint& cons : ret.constraints) {
631  minx = std::min<int>(cons.loc.x, minx);
632  miny = std::min<int>(2 * cons.loc.y + (cons.loc.x & 1), miny);
633  }
634 
635  if((miny & 1) && (minx & 1) && (minx < 0))
636  miny += 2;
637  if(!(miny & 1) && (minx & 1) && (minx > 0))
638  miny -= 2;
639 
640  for(terrain_constraint& cons : ret.constraints) {
641  legacy_sum_assign(cons.loc, map_location(-minx, -((miny - 1) / 2)));
642  }
643 
644  replace_rotate_tokens(ret, angle, rot);
645 }
646 
648  const std::string& variations,
649  const std::chrono::milliseconds& random_start)
650  : image_string(image_string)
651  , variations(variations)
652  , images()
653  , tods()
654  , has_flag()
655  , random_start(random_start)
656 {
657 }
658 
660  const std::string& variations,
661  const std::string& tod,
662  const std::string& has_flag,
663  const std::chrono::milliseconds& random_start)
664  : image_string(image_string)
665  , variations(variations)
666  , images()
667  , tods()
668  , has_flag()
669  , random_start(random_start)
670 {
671  if(!has_flag.empty()) {
672  this->has_flag = utils::split(has_flag);
673  }
674  if(!tod.empty()) {
675  const std::vector<std::string> tod_list = utils::split(tod);
676  tods.insert(tod_list.begin(), tod_list.end());
677  }
678 }
679 
680 void terrain_builder::add_images_from_config(rule_imagelist& images, const config& cfg, bool global, int dx, int dy)
681 {
682  for(const config& img : cfg.child_range("image")) {
683  int layer = img["layer"].to_int();
684 
685  int basex = tilewidth_ / 2 + dx, basey = tilewidth_ / 2 + dy;
686  if(const config::attribute_value* base_ = img.get("base")) {
687  std::vector<std::string> base = utils::split(*base_);
688  if(base.size() >= 2) {
689  try {
690  basex = std::stoi(base[0]);
691  basey = std::stoi(base[1]);
692  } catch(const std::invalid_argument&) {
693  ERR_NG << "Invalid 'base' value in terrain image builder: " << base[0] << ", " << base[1];
694  }
695  }
696  }
697 
698  int center_x = -1, center_y = -1;
699  if(const config::attribute_value* center_ = img.get("center")) {
700  std::vector<std::string> center = utils::split(*center_);
701  if(center.size() >= 2) {
702  try {
703  center_x = std::stoi(center[0]);
704  center_y = std::stoi(center[1]);
705  } catch(const std::invalid_argument&) {
706  ERR_NG << "Invalid 'center' value in terrain image builder: " << center[0] << ", " << center[1];
707  }
708  }
709  }
710 
711  bool is_water = img["is_water"].to_bool();
712 
713  images.AGGREGATE_EMPLACE(layer, basex - dx, basey - dy, global, center_x, center_y, is_water);
714 
715  // Adds the other variants of the image
716  for(const config& variant : img.child_range("variant")) {
717  const std::string& name = variant["name"];
718  const std::string& variations = img["variations"];
719  const std::string& tod = variant["tod"];
720  const std::string& has_flag = variant["has_flag"];
721 
722  // If an integer is given then assign that, but if a bool is given, then assign -1 if true and 0 if false
723  int random_start = variant["random_start"].to_bool(true) ? variant["random_start"].to_int(-1) : 0;
724 
725  images.back().variants.emplace_back(name, variations, tod, has_flag, std::chrono::milliseconds{random_start});
726  }
727 
728  // Adds the main (default) variant of the image at the end,
729  // (will be used only if previous variants don't match)
730  const std::string& name = img["name"];
731  const std::string& variations = img["variations"];
732 
733  int random_start = img["random_start"].to_bool(true) ? img["random_start"].to_int(-1) : 0;
734 
735  images.back().variants.emplace_back(name, variations, std::chrono::milliseconds{random_start});
736  }
737 }
738 
740  const map_location& loc,
742  const config& global_images)
743 {
744  terrain_constraint* cons = nullptr;
745  for(terrain_constraint& c : constraints) {
746  if(c.loc == loc) {
747  cons = &c;
748  break;
749  }
750  }
751 
752  if(!cons) {
753  // The terrain at the current location did not exist, so create it
754  constraints.AGGREGATE_EMPLACE(loc);
755  cons = &constraints.back();
756  }
757 
758  if(!type.terrain.empty()) {
759  cons->terrain_types_match = type;
760  }
761 
762  int x = loc.x * tilewidth_ * 3 / 4;
763  int y = loc.y * tilewidth_ + (loc.x % 2) * tilewidth_ / 2;
764  add_images_from_config(cons->images, global_images, true, x, y);
765 
766  return *cons;
767 }
768 
770  const map_location& loc,
771  const config& cfg,
772  const config& global_images)
773 
774 {
775  // default to WILDCARD overlay in [terrain_graphics] [tile] type=
776  terrain_constraint& constraint = add_constraints(constraints, loc, t_translation::ter_match(cfg["type"].str(), t_translation::WILDCARD), global_images);
777 
778  std::vector<std::string> item_string = utils::square_parenthetical_split(cfg["set_flag"], ',', "[", "]");
779  constraint.set_flag.insert(constraint.set_flag.end(), item_string.begin(), item_string.end());
780 
781  item_string = utils::square_parenthetical_split(cfg["has_flag"], ',', "[", "]");
782  constraint.has_flag.insert(constraint.has_flag.end(), item_string.begin(), item_string.end());
783 
784  item_string = utils::square_parenthetical_split(cfg["no_flag"], ',', "[", "]");
785  constraint.no_flag.insert(constraint.no_flag.end(), item_string.begin(), item_string.end());
786 
787  item_string = utils::square_parenthetical_split(cfg["set_no_flag"], ',', "[", "]");
788  constraint.set_flag.insert(constraint.set_flag.end(), item_string.begin(), item_string.end());
789  constraint.no_flag.insert(constraint.no_flag.end(), item_string.begin(), item_string.end());
790 
791  constraint.no_draw = cfg["no_draw"].to_bool(false);
792 
793  add_images_from_config(constraint.images, cfg, false);
794 }
795 
797  const std::string& mapstring, struct building_rule& br, anchormap& anchors, const config& global_images)
798 {
800 
801  // If there is an empty map leave directly.
802  // Determine after conversion, since a
803  // non-empty string can return an empty map.
804  if(map.data.empty()) {
805  return;
806  }
807 
808  int lineno = (map.get(0, 0) == t_translation::NONE_TERRAIN) ? 1 : 0;
809  int x = lineno;
810  int y = 0;
811  for(int y_off = 0; y_off < map.w; ++y_off) {
812  for(int x_off = x; x_off < map.h; ++x_off) {
813  const t_translation::terrain_code terrain = map.get(y_off, x_off);
814 
815  if(terrain.base == t_translation::TB_DOT) {
816  // Dots are simple placeholders,
817  // which do not represent actual terrains.
818  } else if(terrain.overlay != 0) {
819  anchors.emplace(terrain.overlay, map_location(x, y));
820  } else if(terrain.base == t_translation::TB_STAR) {
821  add_constraints(br.constraints, map_location(x, y), t_translation::STAR, global_images);
822  } else {
823  ERR_NG << "Invalid terrain (" << t_translation::write_terrain_code(terrain) << ") in builder map";
824  assert(false);
825  return;
826  }
827  x += 2;
828  }
829 
830  if(lineno % 2 == 1) {
831  ++y;
832  x = 0;
833  } else {
834  x = 1;
835  }
836  ++lineno;
837  }
838 }
839 
841 {
842  if(load_images(rule)) {
843  rules.insert(rule);
844  }
845 }
846 
847 void terrain_builder::add_rotated_rules(building_ruleset& rules, building_rule& tpl, const std::string& rotations)
848 {
849  if(rotations.empty()) {
850  // Adds the parsed built terrain to the list
851 
852  add_rule(rules, tpl);
853  } else {
854  const std::vector<std::string>& rot = utils::split(rotations, ',');
855 
856  for(std::size_t angle = 0; angle < rot.size(); ++angle) {
857  /* Only 5% of the rules have valid images, so most of
858  them will be discarded. If the ratio was higher,
859  it would be more efficient to insert a copy of the
860  template rule into the ruleset, modify it in place,
861  and remove it if invalid. But since the ratio is so
862  low, the speedup is not worth the extra multiset
863  manipulations. */
864 
865  if(rot.at(angle) == "skip") {
866  continue;
867  }
868 
869  building_rule rule = tpl;
870  rotate_rule(rule, angle, rot);
871  add_rule(rules, rule);
872  }
873  }
874 }
875 
876 void terrain_builder::parse_config(const config& cfg, bool local)
877 {
878  return parse_config(game_config_view::wrap(cfg), local);
879 }
880 
882 {
883  log_scope("terrain_builder::parse_config");
884  int n = 0;
885 
886  // Parses the list of building rules (BRs)
887  for(const config& br : cfg.child_range("terrain_graphics")) {
888  building_rule pbr; // Parsed Building rule
889  pbr.local = local;
890 
891  // add_images_from_config(pbr.images, **br);
892 
893  pbr.location_constraints = map_location(br["x"].to_int() - 1, br["y"].to_int() - 1);
894 
895  pbr.modulo_constraints = map_location(br["mod_x"].to_int(), br["mod_y"].to_int());
896 
897  pbr.probability = br["probability"].to_int(100);
898 
899  // Mapping anchor indices to anchor locations.
900  anchormap anchors;
901 
902  // Parse the map= , if there is one (and fill the anchors list)
903  parse_mapstring(br["map"], pbr, anchors, br);
904 
905  // Parses the terrain constraints (TCs)
906  for(const config& tc : br.child_range("tile")) {
907  // Adds the terrain constraint to the current built terrain's list
908  // of terrain constraints, if it does not exist.
910  if(const config::attribute_value* v = tc.get("x")) {
911  loc.x = v->to_int();
912  }
913  if(const config::attribute_value* v = tc.get("y")) {
914  loc.y = v->to_int();
915  }
916  if(loc.valid()) {
917  add_constraints(pbr.constraints, loc, tc, br);
918  }
919  if(const config::attribute_value* v = tc.get("pos")) {
920  int pos = v->to_int();
921  if(anchors.find(pos) == anchors.end()) {
922  WRN_NG << "Invalid anchor!";
923  continue;
924  }
925 
926  auto range = anchors.equal_range(pos);
927 
928  for(; range.first != range.second; ++range.first) {
929  loc = range.first->second;
930  add_constraints(pbr.constraints, loc, tc, br);
931  }
932  }
933  }
934 
935  const std::vector<std::string> global_set_flag = utils::split(br["set_flag"]);
936  const std::vector<std::string> global_no_flag = utils::split(br["no_flag"]);
937  const std::vector<std::string> global_has_flag = utils::split(br["has_flag"]);
938  const std::vector<std::string> global_set_no_flag = utils::split(br["set_no_flag"]);
939 
940  for(terrain_constraint& constraint : pbr.constraints) {
941  constraint.set_flag.insert(constraint.set_flag.end(), global_set_flag.begin(), global_set_flag.end());
942  constraint.no_flag.insert(constraint.no_flag.end(), global_no_flag.begin(), global_no_flag.end());
943  constraint.has_flag.insert(constraint.has_flag.end(), global_has_flag.begin(), global_has_flag.end());
944  constraint.set_flag.insert(constraint.set_flag.end(), global_set_no_flag.begin(), global_set_no_flag.end());
945  constraint.no_flag.insert(constraint.no_flag.end(), global_set_no_flag.begin(), global_set_no_flag.end());
946  }
947 
948  // Handles rotations
949  const std::string& rotations = br["rotations"];
950 
951  pbr.precedence = br["precedence"].to_int();
952 
953  add_rotated_rules(building_rules_, pbr, rotations);
954 
955  n++;
956  if(n % 10 == 0) {
958  }
959  }
960 
961 // Debug output for the terrain rules
962 #if 0
963  PLAIN_LOG << "Built terrain rules: ";
964 
965  building_ruleset::const_iterator rule;
966  for(rule = building_rules_.begin(); rule != building_rules_.end(); ++rule) {
967  PLAIN_LOG << ">> New rule: image_background = "
968  << "\n>> Location " << rule->second.location_constraints
969  << "\n>> Probability " << rule->second.probability
970 
971  for(constraint_set::const_iterator constraint = rule->second.constraints.begin();
972  constraint != rule->second.constraints.end(); ++constraint) {
973 
974  PLAIN_LOG << ">>>> New constraint: location = (" << constraint->second.loc
975  << "), terrain types = '" << t_translation::write_list(constraint->second.terrain_types_match.terrain) << "'";
976 
977  std::vector<std::string>::const_iterator flag;
978 
979  for(flag = constraint->second.set_flag.begin(); flag != constraint->second.set_flag.end(); ++flag) {
980  PLAIN_LOG << ">>>>>> Set_flag: " << *flag;
981  }
982 
983  for(flag = constraint->second.no_flag.begin(); flag != constraint->second.no_flag.end(); ++flag) {
984  PLAIN_LOG << ">>>>>> No_flag: " << *flag;
985  }
986  }
987 
988  }
989 #endif
990 }
991 
992 void terrain_builder::add_off_map_rule(const std::string& image)
993 {
994  // Build a config object
995  config cfg;
996 
997  config& item = cfg.add_child("terrain_graphics");
998 
999  config& tile = item.add_child("tile");
1000  tile["x"] = 0;
1001  tile["y"] = 0;
1003 
1004  config& tile_image = tile.add_child("image");
1005  tile_image["layer"] = -1000;
1006  tile_image["name"] = image;
1007 
1008  item["probability"] = 100;
1009  item["no_flag"] = "base";
1010  item["set_flag"] = "base";
1011 
1012  // Parse the object
1014 }
1015 
1017  const map_location& loc,
1018  const terrain_constraint* type_checked) const
1019 {
1020  // Don't match if the location isn't a multiple of mod_x and mod_y
1021  if(rule.modulo_constraints.x > 0 && (loc.x % rule.modulo_constraints.x != 0)) {
1022  return false;
1023  }
1024  if(rule.modulo_constraints.y > 0 && (loc.y % rule.modulo_constraints.y != 0)) {
1025  return false;
1026  }
1027 
1028  if(rule.location_constraints.valid() && rule.location_constraints != loc) {
1029  return false;
1030  }
1031 
1032  if(rule.probability != 100) {
1033  unsigned int random = get_noise(loc, rule.get_hash()) % 100;
1034  if(random > static_cast<unsigned int>(rule.probability)) {
1035  return false;
1036  }
1037  }
1038 
1039  for(const terrain_constraint& cons : rule.constraints) {
1040  // Translated location
1041  const map_location tloc = legacy_sum(loc, cons.loc);
1042 
1043  if(!tile_map_.on_map(tloc)) {
1044  return false;
1045  }
1046 
1047  // std::cout << "testing..." << builder_letter(map().get_terrain(tloc))
1048 
1049  // check if terrain matches except if we already know that it does
1050  if(&cons != type_checked && !terrain_matches(map().get_terrain(tloc), cons.terrain_types_match)) {
1051  return false;
1052  }
1053 
1054  const std::set<std::string>& flags = tile_map_[tloc].flags;
1055 
1056  for(const std::string& s : cons.no_flag) {
1057  // If a flag listed in "no_flag" is present, the rule does not match
1058  if(flags.find(s) != flags.end()) {
1059  return false;
1060  }
1061  }
1062  for(const std::string& s : cons.has_flag) {
1063  // If a flag listed in "has_flag" is not present, this rule does not match
1064  if(flags.find(s) == flags.end()) {
1065  return false;
1066  }
1067  }
1068  }
1069 
1070  return true;
1071 }
1072 
1074 {
1075  unsigned int rand_seed = get_noise(loc, rule.get_hash());
1076 
1077  for(const terrain_constraint& constraint : rule.constraints) {
1078  const map_location tloc = legacy_sum(loc, constraint.loc);
1079  if(!tile_map_.on_map(tloc)) {
1080  return;
1081  }
1082 
1083  tile& btile = tile_map_[tloc];
1084 
1085  if(!constraint.no_draw) {
1086  for(const rule_image& img : constraint.images) {
1087  btile.images.AGGREGATE_EMPLACE(&img, rand_seed);
1088  }
1089  }
1090 
1091  // Sets flags
1092  for(const std::string& flag : constraint.set_flag) {
1093  btile.flags.insert(flag);
1094  }
1095  }
1096 }
1097 
1098 // copied from text_surface::hash()
1099 // but keep it separated because the needs are different
1100 // and changing it will modify the map random variations
1101 static unsigned int hash_str(const std::string& str)
1102 {
1103  unsigned int h = 0;
1104  for(std::string::const_iterator it = str.begin(), it_end = str.end(); it != it_end; ++it)
1105  h = ((h << 9) | (h >> (sizeof(int) * 8 - 9))) ^ (*it);
1106  return h;
1107 }
1108 
1110 {
1111  if(hash_ != DUMMY_HASH)
1112  return hash_;
1113 
1114  for(const terrain_constraint& constraint : constraints) {
1115  for(const rule_image& ri : constraint.images) {
1116  for(const rule_image_variant& variant : ri.variants) {
1117  // we will often hash the same string, but that seems fast enough
1118  hash_ += hash_str(variant.image_string);
1119  }
1120  }
1121  }
1122 
1123  // don't use the reserved dummy hash
1124  if(hash_ == DUMMY_HASH)
1125  hash_ = 105533; // just a random big prime number
1126 
1127  return hash_;
1128 }
1129 
1131 {
1132  log_scope("terrain_builder::build_terrains");
1133 
1134  // Builds the terrain_by_type_ cache
1135  for(int x = -2; x <= map().w(); ++x) {
1136  for(int y = -2; y <= map().h(); ++y) {
1137  const map_location loc(x, y);
1139 
1140  terrain_by_type_[t].push_back(loc);
1141 
1142  // Flag all hexes according to whether they're on the border or not,
1143  // to make it easier for WML to draw the borders
1144  if(draw_border_&& !map().on_board(loc)) {
1145  tile_map_[loc].flags.insert("_border");
1146  } else {
1147  tile_map_[loc].flags.insert("_board");
1148  }
1149  }
1150  }
1151 
1152  for(const building_rule& rule : building_rules_) {
1153  // Find the constraint that contains the less terrain of all terrain rules.
1154  // We will keep a track of the matching terrains of this constraint
1155  // and later try to apply the rule only on them
1156  std::size_t min_size = std::numeric_limits<int>::max();
1157  t_translation::ter_list min_types = t_translation::ter_list(); // <-- This must be explicitly initialized, just
1158  // as min_constraint is, at start of loop, or we
1159  // get a null pointer dereference when we go
1160  // through on later times.
1161  const terrain_constraint* min_constraint = nullptr;
1162 
1163  for(const terrain_constraint& constraint : rule.constraints) {
1164  const t_translation::ter_match& match = constraint.terrain_types_match;
1165  t_translation::ter_list matching_types;
1166  std::size_t constraint_size = 0;
1167 
1168  for(terrain_by_type_map::iterator type_it = terrain_by_type_.begin(); type_it != terrain_by_type_.end();
1169  ++type_it) {
1170  const t_translation::terrain_code t = type_it->first;
1171  if(terrain_matches(t, match)) {
1172  const std::size_t match_size = type_it->second.size();
1173  constraint_size += match_size;
1174  if(constraint_size >= min_size) {
1175  break; // not a minimum, bail out
1176  }
1177  matching_types.push_back(t);
1178  }
1179  }
1180 
1181  if(constraint_size < min_size) {
1182  min_size = constraint_size;
1183  min_types = matching_types;
1184  min_constraint = &constraint;
1185  if(min_size == 0) {
1186  // a constraint is never matched on this map
1187  // we break with a empty type list
1188  break;
1189  }
1190  }
1191  }
1192 
1193  assert(min_constraint != nullptr);
1194 
1195  // NOTE: if min_types is not empty, we have found a valid min_constraint;
1196  for(const auto& t : min_types) {
1197  for(map_location mloc : terrain_by_type_[t]) {
1198  const map_location loc = legacy_difference(mloc, min_constraint->loc);
1199 
1200  if(rule_matches(rule, loc, min_constraint)) {
1201  apply_rule(rule, loc);
1202  }
1203  }
1204  }
1205  }
1206 }
1207 
1209 {
1210  if(tile_map_.on_map(loc))
1211  return &(tile_map_[loc]);
1212  return nullptr;
1213 }
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:396
static unsigned int hash_str(const std::string &str)
Definition: builder.cpp:1101
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:383
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 add_frame(const std::chrono::milliseconds &duration, const T &value, bool force_change=false)
Appends a frame, starting where the previous one ends (or at start_time, if first).
void start_animation(const std::chrono::milliseconds &start_time_offset, bool cycles=false)
Anchors the timeline to the current tick, then begins playback.
bool need_update() const
True once the current tick has reached the current frame's end, meaning advance_to_current_frame() ha...
std::size_t get_frames_count() const
std::chrono::milliseconds get_animation_duration() const
get_end_time() minus get_begin_time(): how long one pass through all frames takes.
void advance_to_current_frame()
Moves current_frame_index_ forward to match the current tick, one frame at a time (or in whole cycles...
Variant for storing WML attributes.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:157
config & add_child(std::string_view key)
Definition: config.cpp:436
child_itors child_range(std::string_view key)
Definition: config.cpp:268
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)
terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition: map.cpp:265
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:68
Encapsulates the map of the game.
Definition: map.hpp:176
const terrain_type & get_terrain_info(const t_translation::terrain_code &terrain) const
Definition: map.cpp:78
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:796
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:876
tile * get_tile(const map_location &loc)
Definition: builder.cpp:1208
void add_rule(building_ruleset &rules, building_rule &rule)
Adds a rule to a ruleset.
Definition: builder.cpp:840
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:739
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:615
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:992
void build_terrains()
Calculates the list of terrains, and fills the tile_map_ member, from the gamemap and the building_ru...
Definition: builder.cpp:1130
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:680
void rebuild_terrain(const map_location &loc)
Performs a "quick-rebuild" of the terrain in a given location.
Definition: builder.cpp:346
bool update_animation(const map_location &loc)
Updates the animation at a given tile.
Definition: builder.cpp:320
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:1016
void change_map(const gamemap *m)
Definition: builder.cpp:292
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:298
bool load_images(building_rule &rule)
Load images and tests for validity of a rule.
Definition: builder.cpp:422
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:1073
void flush_local_rules()
Definition: builder.cpp:268
void rotate(terrain_constraint &constraint, int angle)
"Rotates" a constraint from a rule.
Definition: builder.cpp:482
void rebuild_all()
Performs a complete rebuild of the list of terrain graphics attached to a map.
Definition: builder.cpp:376
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:847
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:285
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:562
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
const config * cfg
std::size_t i
Definition: function.cpp:1031
Standard logging facilities (interface).
#define PLAIN_LOG
Definition: log.hpp:296
#define log_scope(description)
Definition: log.hpp:275
Game configuration data as global variables.
Definition: build_info.cpp:68
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:835
bool precached_file_exists(const std::string &file)
Definition: picture.cpp:911
bool exists(const image::locator &i_locator)
Returns true if the given image actually exists, without loading it.
Definition: picture.cpp:855
void precache_file_existence(const std::string &subdir)
Precache the existence of files in a binary path subdirectory (e.g.
Definition: picture.cpp:904
std::string img(const std::string &src, const std::string &align, bool floating)
Generates a Help markup tag corresponding to an image.
Definition: markup.cpp:37
constexpr terrain_code NONE_TERRAIN
Definition: translation.hpp:58
const ter_layer TB_STAR
constexpr 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.
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:156
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
int w
Definition: pathfind.cpp:188
std::string filename
Filename.
Encapsulates the map of the game.
Definition: location.hpp:46
bool valid() const
Definition: location.hpp:111
This structure can be used for matching terrain strings.
Definition: translation.hpp:98
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:1109
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:647
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