The Battle for Wesnoth  1.17.6+dev
display.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2022
3  by David White <dave@whitevine.net>
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  * Routines to set up the display, scroll and zoom the map.
19  */
20 
21 #include "arrow.hpp"
22 #include "color.hpp"
23 #include "cursor.hpp"
24 #include "display.hpp"
25 #include "draw.hpp"
26 #include "draw_manager.hpp"
27 #include "fake_unit_manager.hpp"
28 #include "filesystem.hpp"
29 #include "font/sdl_ttf_compat.hpp"
30 #include "font/text.hpp"
31 #include "preferences/game.hpp"
32 #include "gettext.hpp"
34 #include "halo.hpp"
36 #include "language.hpp"
37 #include "log.hpp"
38 #include "map/map.hpp"
39 #include "map/label.hpp"
40 #include "minimap.hpp"
41 #include "overlay.hpp"
42 #include "play_controller.hpp" //note: this can probably be refactored out
43 #include "reports.hpp"
44 #include "resources.hpp"
45 #include "sdl/utils.hpp" // fill_surface_rect
46 #include "show_dialog.hpp"
47 #include "synced_context.hpp"
48 #include "team.hpp"
49 #include "terrain/builder.hpp"
50 #include "time_of_day.hpp"
51 #include "tooltips.hpp"
52 #include "tod_manager.hpp"
53 #include "units/unit.hpp"
55 #include "units/drawer.hpp"
56 #include "units/orb_status.hpp"
57 #include "video.hpp"
58 #include "whiteboard/manager.hpp"
59 
60 #include <SDL2/SDL_image.h>
61 
62 #include <algorithm>
63 #include <array>
64 #include <cmath>
65 #include <iomanip>
66 #include <numeric>
67 #include <utility>
68 
69 #ifdef _WIN32
70 #include <windows.h>
71 #endif
72 
73 // Includes for bug #17573
74 
75 static lg::log_domain log_display("display");
76 #define ERR_DP LOG_STREAM(err, log_display)
77 #define WRN_DP LOG_STREAM(warn, log_display)
78 #define LOG_DP LOG_STREAM(info, log_display)
79 #define DBG_DP LOG_STREAM(debug, log_display)
80 
81 // These are macros instead of proper constants so that they auto-update if the game config is reloaded.
82 #define zoom_levels (game_config::zoom_levels)
83 #define final_zoom_index (static_cast<int>(zoom_levels.size()) - 1)
84 #define DefaultZoom (game_config::tile_size)
85 #define SmallZoom (DefaultZoom / 2)
86 #define MinZoom (zoom_levels.front())
87 #define MaxZoom (zoom_levels.back())
88 
89 namespace {
90  // if this is enabled with :benchmark, then everything is marked as invalid and redrawn each time
91  bool benchmark = false;
92 
93  bool debug_foreground = false;
94 
95  int prevLabel = 0;
96 }
97 
98 unsigned int display::zoom_ = DefaultZoom;
99 unsigned int display::last_zoom_ = SmallZoom;
100 
101 // Returns index of zoom_levels which is closest match to input zoom_level
102 // Assumption: zoom_levels is a sorted vector of ascending tile sizes
103 static int get_zoom_levels_index(unsigned int zoom_level)
104 {
105  zoom_level = std::clamp(zoom_level, MinZoom, MaxZoom); // ensure zoom_level is within zoom_levels bounds
106  auto iter = std::lower_bound(zoom_levels.begin(), zoom_levels.end(), zoom_level);
107 
108  // find closest match
109  if(iter != zoom_levels.begin() && iter != zoom_levels.end()) {
110  float diff = *iter - *(iter - 1);
111  float lower = (zoom_level - *(iter - 1)) / diff;
112  float upper = (*iter - zoom_level) / diff;
113 
114  // the previous element is closer to zoom_level than the current one
115  if(lower < upper) {
116  iter--;
117  }
118  }
119 
120  return std::distance(zoom_levels.begin(), iter);
121 }
122 
124 {
125  const team& curr_team = dc_->teams()[playing_team()];
126  const team& prev_team = playing_team() == 0
127  ? dc_->teams().back()
128  : dc_->get_team(playing_team());
129  for (const auto& i : get_overlays()) {
130  for(const overlay& ov : i.second) {
131  if(!ov.team_name.empty() &&
132  ((ov.team_name.find(curr_team.team_name()) + 1) != 0) !=
133  ((ov.team_name.find(prev_team.team_name()) + 1) != 0))
134  {
135  invalidate(i.first);
136  }
137  }
138  }
139 }
140 
141 
142 void display::add_overlay(const map_location& loc, const std::string& img, const std::string& halo, const std::string& team_name, const std::string& item_id, bool visible_under_fog, float submerge, float z_order)
143 {
144  halo::handle halo_handle;
145  if(halo != "") {
146  halo_handle = halo_man_.add(get_location_x(loc) + hex_size() / 2,
147  get_location_y(loc) + hex_size() / 2, halo, loc);
148  }
149 
150  std::vector<overlay>& overlays = get_overlays()[loc];
151  auto it = std::find_if(overlays.begin(), overlays.end(), [z_order](const overlay& ov) { return ov.z_order > z_order; });
152  overlays.emplace(it, img, halo, halo_handle, team_name, item_id, visible_under_fog, submerge, z_order);
153 }
154 
156 {
157  get_overlays().erase(loc);
158 }
159 
160 void display::remove_single_overlay(const map_location& loc, const std::string& toDelete)
161 {
162  std::vector<overlay>& overlays = get_overlays()[loc];
163  overlays.erase(
164  std::remove_if(
165  overlays.begin(), overlays.end(),
166  [&toDelete](const overlay& ov) { return ov.image == toDelete || ov.halo == toDelete || ov.id == toDelete; }
167  ),
168  overlays.end()
169  );
170 }
171 
173  std::weak_ptr<wb::manager> wb,
174  reports& reports_object,
175  const std::string& theme_id,
176  const config& level)
177  : dc_(dc)
178  , halo_man_()
179  , wb_(wb)
181  , currentTeam_(0)
182  , dont_show_all_(false)
183  , xpos_(0)
184  , ypos_(0)
185  , view_locked_(false)
186  , theme_(theme::get_theme_config(theme_id.empty() ? preferences::theme() : theme_id), video::game_canvas())
187  , zoom_index_(0)
188  , fake_unit_man_(new fake_unit_manager(*this))
189  , builder_(new terrain_builder(level, (dc_ ? &dc_->map() : nullptr), theme_.border().tile_image, theme_.border().show_border))
190  , minimap_(nullptr)
192  , redraw_background_(false)
193  , invalidateAll_(true)
194  , diagnostic_label_(0)
195  , invalidateGameStatus_(true)
196  , map_labels_(new map_labels(nullptr))
197  , reports_object_(&reports_object)
198  , scroll_event_("scrolled")
199  , frametimes_(50)
200  , fps_counter_()
201  , fps_start_()
202  , fps_actual_()
203  , reportLocations_()
204  , reportSurfaces_()
205  , reports_()
206  , menu_buttons_()
207  , action_buttons_()
208  , invalidated_()
210  , tod_hex_mask1(nullptr)
211  , tod_hex_mask2(nullptr)
212  , fog_images_()
213  , shroud_images_()
214  , selectedHex_()
215  , mouseoverHex_()
216  , keys_()
217  , animate_map_(true)
218  , animate_water_(true)
219  , flags_()
220  , activeTeam_(0)
221  , drawing_buffer_()
222  , map_screenshot_(false)
223  , reach_map_()
224  , reach_map_old_()
225  , reach_map_changed_(true)
226  , fps_handle_(0)
227  , invalidated_hexes_(0)
228  , drawn_hexes_(0)
230  , draw_coordinates_(false)
231  , draw_terrain_codes_(false)
232  , draw_num_of_bitmaps_(false)
233  , arrows_map_()
234  , color_adjust_()
235 {
236  //The following assertion fails when starting a campaign
237  assert(singleton_ == nullptr);
238  singleton_ = this;
239 
241 
242  blindfold_ctr_ = 0;
243 
244  read(level.child_or_empty("display"));
245 
248 
249  unsigned int tile_size = preferences::tile_size();
250  if(tile_size < MinZoom || tile_size > MaxZoom)
251  tile_size = DefaultZoom;
252  zoom_index_ = get_zoom_levels_index(tile_size);
254  if(zoom_ != preferences::tile_size()) // correct saved tile_size if necessary
256 
258 
259  init_flags();
260 
261  if(!menu_buttons_.empty() || !action_buttons_.empty()) {
262  create_buttons();
263  }
264 
265 #ifdef _WIN32
266  // Increase timer resolution to prevent delays getting much longer than they should.
267  timeBeginPeriod(1u);
268 #endif
269 }
270 
272 {
273 #ifdef _WIN32
274  timeEndPeriod(1u);
275 #endif
276 
277  singleton_ = nullptr;
278  resources::fake_units = nullptr;
279 }
280 
281 void display::set_theme(const std::string& new_theme)
282 {
284  builder_->set_draw_border(theme_.border().show_border);
285  menu_buttons_.clear();
286  action_buttons_.clear();
287  create_buttons();
288  rebuild_all();
289  queue_rerender();
290 }
291 
293 
294  flags_.clear();
295  if (!dc_) return;
296  flags_.resize(dc_->teams().size());
297 
298  std::vector<std::string> side_colors;
299  side_colors.reserve(dc_->teams().size());
300 
301  for(const team& t : dc_->teams()) {
302  std::string side_color = t.color();
303  side_colors.push_back(side_color);
304  init_flags_for_side_internal(t.side() - 1, side_color);
305  }
306 }
307 
309 {
311 }
312 
313 void display::init_flags_for_side_internal(std::size_t n, const std::string& side_color)
314 {
315  assert(dc_ != nullptr);
316  assert(n < dc_->teams().size());
317  assert(n < flags_.size());
318 
319  std::string flag = dc_->teams()[n].flag();
320  std::string old_rgb = game_config::flag_rgb;
321  std::string new_rgb = side_color;
322 
323  if(flag.empty()) {
325  }
326 
327  LOG_DP << "Adding flag for team " << n << " from animation " << flag;
328 
329  // Must recolor flag image
330  animated<image::locator> temp_anim;
331 
332  std::vector<std::string> items = utils::square_parenthetical_split(flag);
333 
334  for(const std::string& item : items) {
335  const std::vector<std::string>& sub_items = utils::split(item, ':');
336  std::string str = item;
337  int time = 100;
338 
339  if(sub_items.size() > 1) {
340  str = sub_items.front();
341  try {
342  time = std::max<int>(1, std::stoi(sub_items.back()));
343  } catch(const std::invalid_argument&) {
344  ERR_DP << "Invalid time value found when constructing flag for side " << n << ": " << sub_items.back();
345  }
346  }
347 
348  std::stringstream temp;
349  temp << str << "~RC(" << old_rgb << ">"<< new_rgb << ")";
350  image::locator flag_image(temp.str());
351  temp_anim.add_frame(time, flag_image);
352  }
353 
355  f = temp_anim;
356  auto time = f.get_end_time();
357  if (time > 0) {
358  f.start_animation(randomness::rng::default_instance().get_random_int(0, time-1), true);
359  }
360  else {
361  // this can happen if both flag and game_config::images::flag are empty.
362  ERR_DP << "missing flag for team" << n;
363  }
364 }
365 
367 {
368  if(!get_map().is_village(loc)) {
369  return texture();
370  }
371 
372  for (const team& t : dc_->teams()) {
373  if (t.owns_village(loc) && (!fogged(loc) || !dc_->get_team(viewing_side()).is_enemy(t.side())))
374  {
375  auto& flag = flags_[t.side() - 1];
376  flag.update_last_draw_time();
377  const image::locator &image_flag = animate_map_ ?
378  flag.get_current_frame() : flag.get_first_frame();
379  return image::get_texture(image_flag, image::TOD_COLORED);
380  }
381  }
382 
383  return texture();
384 }
385 
386 void display::set_team(std::size_t teamindex, bool show_everything)
387 {
388  assert(teamindex < dc_->teams().size());
389  currentTeam_ = teamindex;
390  if(!show_everything) {
391  labels().set_team(&dc_->teams()[teamindex]);
392  dont_show_all_ = true;
393  } else {
394  labels().set_team(nullptr);
395  dont_show_all_ = false;
396  }
398  if(std::shared_ptr<wb::manager> w = wb_.lock()) {
399  w->on_viewer_change(teamindex);
400  }
401 }
402 
403 void display::set_playing_team(std::size_t teamindex)
404 {
405  assert(teamindex < dc_->teams().size());
406  activeTeam_ = teamindex;
408 }
409 
411 {
412  if(loc.valid() && exclusive_unit_draw_requests_.find(loc) == exclusive_unit_draw_requests_.end()) {
413  exclusive_unit_draw_requests_[loc] = unit.id();
414  return true;
415  } else {
416  return false;
417  }
418 }
419 
421 {
422  std::string id = "";
423  if(loc.valid())
424  {
426  //id will be set to the default "" string by the [] operator if the map doesn't have anything for that loc.
428  }
429  return id;
430 }
431 
432 const time_of_day & display::get_time_of_day(const map_location& /*loc*/) const
433 {
434  static time_of_day tod;
435  return tod;
436 }
437 
438 void display::update_tod(const time_of_day* tod_override)
439 {
440  const time_of_day* tod = tod_override;
441  if(tod == nullptr) {
442  tod = &get_time_of_day();
443  }
444 
445  const tod_color col = color_adjust_ + tod->color;
446  image::set_color_adjustment(col.r, col.g, col.b);
447 
448  invalidate_all();
449 }
450 
451 void display::adjust_color_overlay(int r, int g, int b)
452 {
453  color_adjust_ = tod_color(r, g, b);
454  update_tod();
455 }
456 
457 void display::fill_images_list(const std::string& prefix, std::vector<std::string>& images)
458 {
459  if(prefix == ""){
460  return;
461  }
462 
463  // search prefix.png, prefix1.png, prefix2.png ...
464  for(int i=0; ; ++i){
465  std::ostringstream s;
466  s << prefix;
467  if(i != 0)
468  s << i;
469  s << ".png";
470  if(image::exists(s.str()))
471  images.push_back(s.str());
472  else if(i>0)
473  break;
474  }
475  if (images.empty())
476  images.emplace_back();
477 }
478 
479 const std::string& display::get_variant(const std::vector<std::string>& variants, const map_location &loc)
480 {
481  //TODO use better noise function
482  return variants[std::abs(loc.x + loc.y) % variants.size()];
483 }
484 
486 {
487  builder_->rebuild_all();
488 }
489 
491 {
492  redraw_background_ = true;
493  builder_->reload_map();
494 }
495 
497 {
498  dc_ = dc;
499  builder_->change_map(&dc_->map()); //TODO: Should display_context own and initialize the builder object?
500 }
501 
502 void display::blindfold(bool value)
503 {
504  if(value == true)
505  ++blindfold_ctr_;
506  else
507  --blindfold_ctr_;
508 }
509 
511 {
512  return blindfold_ctr_ > 0;
513 }
514 
516 {
518 }
519 
521 {
523 }
524 
526 {
528 }
529 
531 {
532  rect max_area{0, 0, 0, 0};
533 
534  // hex_size() is always a multiple of 4
535  // and hex_width() a multiple of 3,
536  // so there shouldn't be off-by-one-errors
537  // due to rounding.
538  // To display a hex fully on screen,
539  // a little bit extra space is needed.
540  // Also added the border two times.
541  max_area.w = static_cast<int>((get_map().w() + 2 * theme_.border().size + 1.0 / 3.0) * hex_width());
542  max_area.h = static_cast<int>((get_map().h() + 2 * theme_.border().size + 0.5) * hex_size());
543 
544  return max_area;
545 }
546 
548 {
549  rect max_area = max_map_area();
550 
551  // if it's for map_screenshot, maximize and don't recenter
552  if(map_screenshot_) {
553  return max_area;
554  }
555 
556  rect res = map_outside_area();
557 
558  if(max_area.w < res.w) {
559  // map is smaller, center
560  res.x += (res.w - max_area.w) / 2;
561  res.w = max_area.w;
562  }
563 
564  if(max_area.h < res.h) {
565  // map is smaller, center
566  res.y += (res.h - max_area.h) / 2;
567  res.h = max_area.h;
568  }
569 
570  return res;
571 }
572 
574 {
575  if(map_screenshot_) {
576  return max_map_area();
577  } else {
579  }
580 }
581 
582 bool display::outside_area(const SDL_Rect& area, const int x, const int y)
583 {
584  const int x_thresh = hex_size();
585  const int y_thresh = hex_size();
586  return (x < area.x || x > area.x + area.w - x_thresh || y < area.y || y > area.y + area.h - y_thresh);
587 }
588 
589 // This function uses the screen as reference
590 const map_location display::hex_clicked_on(int xclick, int yclick) const
591 {
592  rect r = map_area();
593  if(!r.contains(xclick, yclick)) {
594  return map_location();
595  }
596 
597  xclick -= r.x;
598  yclick -= r.y;
599 
600  return pixel_position_to_hex(xpos_ + xclick, ypos_ + yclick);
601 }
602 
603 // This function uses the rect of map_area as reference
605 {
606  // adjust for the border
607  x -= static_cast<int>(theme_.border().size * hex_width());
608  y -= static_cast<int>(theme_.border().size * hex_size());
609  // The editor can modify the border and this will result in a negative y
610  // value. Instead of adding extra cases we just shift the hex. Since the
611  // editor doesn't use the direction this is no problem.
612  const int offset = y < 0 ? 1 : 0;
613  if(offset) {
614  x += hex_width();
615  y += hex_size();
616  }
617  const int s = hex_size();
618  const int tesselation_x_size = hex_width() * 2;
619  const int tesselation_y_size = s;
620  const int x_base = x / tesselation_x_size * 2;
621  const int x_mod = x % tesselation_x_size;
622  const int y_base = y / tesselation_y_size;
623  const int y_mod = y % tesselation_y_size;
624 
625  int x_modifier = 0;
626  int y_modifier = 0;
627 
628  if (y_mod < tesselation_y_size / 2) {
629  if ((x_mod * 2 + y_mod) < (s / 2)) {
630  x_modifier = -1;
631  y_modifier = -1;
632  } else if ((x_mod * 2 - y_mod) < (s * 3 / 2)) {
633  x_modifier = 0;
634  y_modifier = 0;
635  } else {
636  x_modifier = 1;
637  y_modifier = -1;
638  }
639 
640  } else {
641  if ((x_mod * 2 - (y_mod - s / 2)) < 0) {
642  x_modifier = -1;
643  y_modifier = 0;
644  } else if ((x_mod * 2 + (y_mod - s / 2)) < s * 2) {
645  x_modifier = 0;
646  y_modifier = 0;
647  } else {
648  x_modifier = 1;
649  y_modifier = 0;
650  }
651  }
652 
653  return map_location(x_base + x_modifier - offset, y_base + y_modifier - offset);
654 }
655 
657 {
658  if (loc_.y < rect_.bottom[loc_.x & 1])
659  ++loc_.y;
660  else {
661  ++loc_.x;
662  loc_.y = rect_.top[loc_.x & 1];
663  }
664 
665  return *this;
666 }
667 
668 // begin is top left, and end is after bottom right
670 {
671  return iterator(map_location(left, top[left & 1]), *this);
672 }
674 {
675  return iterator(map_location(right+1, top[(right+1) & 1]), *this);
676 }
677 
678 const display::rect_of_hexes display::hexes_under_rect(const SDL_Rect& r) const
679 {
680  rect_of_hexes res;
681 
682  if(r.w <= 0 || r.h <= 0) {
683  // empty rect, return dummy values giving begin=end
684  res.left = 0;
685  res.right = -1; // end is right+1
686  res.top[0] = 0;
687  res.top[1] = 0;
688  res.bottom[0] = 0;
689  res.bottom[1] = 0;
690  return res;
691  }
692 
693  SDL_Rect map_rect = map_area();
694  // translate rect coordinates from screen-based to map_area-based
695  int x = xpos_ - map_rect.x + r.x;
696  int y = ypos_ - map_rect.y + r.y;
697  // we use the "double" type to avoid important rounding error (size of an hex!)
698  // we will also need to use std::floor to avoid bad rounding at border (negative values)
699  double tile_width = hex_width();
700  double tile_size = hex_size();
701  double border = theme_.border().size;
702  // we minus "0.(3)", for horizontal imbrication.
703  // reason is: two adjacent hexes each overlap 1/4 of their width, so for
704  // grid calculation 3/4 of tile width is used, which by default gives
705  // 18/54=0.(3). Note that, while tile_width is zoom dependent, 0.(3) is not.
706  res.left = static_cast<int>(std::floor(-border + x / tile_width - 0.3333333));
707  // we remove 1 pixel of the rectangle dimensions
708  // (the rounded division take one pixel more than needed)
709  res.right = static_cast<int>(std::floor(-border + (x + r.w - 1) / tile_width));
710 
711  // for odd x, we must shift up one half-hex. Since x will vary along the edge,
712  // we store here the y values for even and odd x, respectively
713  res.top[0] = static_cast<int>(std::floor(-border + y / tile_size));
714  res.top[1] = static_cast<int>(std::floor(-border + y / tile_size - 0.5));
715  res.bottom[0] = static_cast<int>(std::floor(-border + (y + r.h - 1) / tile_size));
716  res.bottom[1] = static_cast<int>(std::floor(-border + (y + r.h - 1) / tile_size - 0.5));
717 
718  // TODO: in some rare cases (1/16), a corner of the big rect is on a tile
719  // (the 72x72 rectangle containing the hex) but not on the hex itself
720  // Can maybe be optimized by using pixel_position_to_hex
721 
722  return res;
723 }
724 
726 {
727  return currentTeam_ < dc_->teams().size();
728 }
729 
730 bool display::shrouded(const map_location& loc) const
731 {
732  return is_blindfolded() || (dont_show_all_ && dc_->teams()[currentTeam_].shrouded(loc));
733 }
734 
735 bool display::fogged(const map_location& loc) const
736 {
737  return is_blindfolded() || (dont_show_all_ && dc_->teams()[currentTeam_].fogged(loc));
738 }
739 
741 {
742  return static_cast<int>(map_area().x + (loc.x + theme_.border().size) * hex_width() - xpos_);
743 }
744 
746 {
747  return static_cast<int>(map_area().y + (loc.y + theme_.border().size) * zoom_ - ypos_ + (is_odd(loc.x) ? zoom_/2 : 0));
748 }
749 
751 {
752  // TODO: don't return location for this,
753  // instead directly scroll to the clicked pixel position
754 
755  if(!minimap_area().contains(x, y)) {
756  return map_location();
757  }
758 
759  // we transform the coordinates from minimap to the full map image
760  // probably more adjustments to do (border, minimap shift...)
761  // but the mouse and human capacity to evaluate the rectangle center
762  // is not pixel precise.
763  int px = (x - minimap_location_.x) * get_map().w() * hex_width() / std::max(minimap_location_.w, 1);
764  int py = (y - minimap_location_.y) * get_map().h() * hex_size() / std::max(minimap_location_.h, 1);
765 
766  map_location loc = pixel_position_to_hex(px, py);
767  if(loc.x < 0) {
768  loc.x = 0;
769  } else if(loc.x >= get_map().w()) {
770  loc.x = get_map().w() - 1;
771  }
772 
773  if(loc.y < 0) {
774  loc.y = 0;
775  } else if(loc.y >= get_map().h()) {
776  loc.y = get_map().h() - 1;
777  }
778 
779  return loc;
780 }
781 
782 surface display::screenshot(bool map_screenshot)
783 {
784  if (!map_screenshot) {
785  LOG_DP << "taking ordinary screenshot";
786  return video::read_pixels();
787  }
788 
789  if (get_map().empty()) {
790  ERR_DP << "No map loaded, cannot create a map screenshot.";
791  return nullptr;
792  }
793 
794  // back up the current map view position and move to top-left
795  int old_xpos = xpos_;
796  int old_ypos = ypos_;
797  xpos_ = 0;
798  ypos_ = 0;
799 
800  // Reroute render output to a separate texture until the end of scope.
801  SDL_Rect area = max_map_area();
802  if (area.w > 1 << 16 || area.h > 1 << 16) {
803  WRN_DP << "Excessively large map screenshot area";
804  }
805  LOG_DP << "creating " << area.w << " by " << area.h
806  << " texture for map screenshot";
807  texture output_texture(area.w, area.h, SDL_TEXTUREACCESS_TARGET);
808  auto target_setter = draw::set_render_target(output_texture);
809  auto clipper = draw::override_clip(area);
810 
811  map_screenshot_ = true;
812 
814  draw();
815 
816  map_screenshot_ = false;
817 
818  // Restore map viewport position
819  xpos_ = old_xpos;
820  ypos_ = old_ypos;
821 
822  // Read rendered pixels back as an SDL surface.
823  return video::read_pixels();
824 }
825 
826 std::shared_ptr<gui::button> display::find_action_button(const std::string& id)
827 {
828  for(auto& b : action_buttons_) {
829  if(b->id() == id) {
830  return b;
831  }
832  }
833  return nullptr;
834 }
835 
836 std::shared_ptr<gui::button> display::find_menu_button(const std::string& id)
837 {
838  for(auto& b : menu_buttons_) {
839  if(b->id() == id) {
840  return b;
841  }
842  }
843  return nullptr;
844 }
845 
847 {
848  DBG_DP << "positioning menu buttons...";
849  for(const auto& menu : theme_.menus()) {
850  if(auto b = find_menu_button(menu.get_id())) {
851  const rect& loc = menu.location(video::game_canvas());
852  b->set_location(loc);
853  b->set_measurements(0,0);
854  b->set_label(menu.title());
855  b->set_image(menu.image());
856  }
857  }
858 
859  DBG_DP << "positioning action buttons...";
860  for(const auto& action : theme_.actions()) {
861  if(auto b = find_action_button(action.get_id())) {
862  const rect& loc = action.location(video::game_canvas());
863  b->set_location(loc);
864  b->set_measurements(0,0);
865  b->set_label(action.title());
866  b->set_image(action.image());
867  }
868  }
869 }
870 
871 namespace
872 {
873 gui::button::TYPE string_to_button_type(const std::string& type)
874 {
875  if(type == "checkbox") {
877  } else if(type == "image") {
879  } else if(type == "radiobox") {
881  } else if(type == "turbo") {
883  } else {
885  }
886 }
887 
888 const std::string& get_direction(std::size_t n)
889 {
890  using namespace std::literals::string_literals;
891  static const std::array dirs{"-n"s, "-ne"s, "-se"s, "-s"s, "-sw"s, "-nw"s};
892  return dirs[n >= dirs.size() ? 0 : n];
893 }
894 } // namespace
895 
897 {
898  if(video::headless()) {
899  return;
900  }
901 
902  menu_buttons_.clear();
903  action_buttons_.clear();
904 
905  DBG_DP << "creating menu buttons...";
906  for(const auto& menu : theme_.menus()) {
907  if(!menu.is_button()) {
908  continue;
909  }
910 
911  auto b = std::make_shared<gui::button>(menu.title(), gui::button::TYPE_PRESS, menu.image(),
912  gui::button::DEFAULT_SPACE, true, menu.overlay(), font::SIZE_BUTTON_SMALL);
913 
914  DBG_DP << "drawing button " << menu.get_id();
915  b->set_id(menu.get_id());
916  if(!menu.tooltip().empty()) {
917  b->set_tooltip_string(menu.tooltip());
918  }
919 
920  if(auto b_prev = find_menu_button(b->id())) {
921  b->enable(b_prev->enabled());
922  }
923 
924  menu_buttons_.push_back(std::move(b));
925  }
926 
927  DBG_DP << "creating action buttons...";
928  for(const auto& action : theme_.actions()) {
929  auto b = std::make_shared<gui::button>(action.title(), string_to_button_type(action.type()),
930  action.image(), gui::button::DEFAULT_SPACE, true, action.overlay(), font::SIZE_BUTTON_SMALL);
931 
932  DBG_DP << "drawing button " << action.get_id();
933  b->set_id(action.get_id());
934  if(!action.tooltip(0).empty()) {
935  b->set_tooltip_string(action.tooltip(0));
936  }
937 
938  if(auto b_prev = find_action_button(b->id())) {
939  b->enable(b_prev->enabled());
940  if(b_prev->get_type() == gui::button::TYPE_CHECK) {
941  b->set_check(b_prev->checked());
942  }
943  }
944 
945  action_buttons_.push_back(std::move(b));
946  }
947 
948  layout_buttons();
949  DBG_DP << "buttons created";
950 }
951 
953 {
954  // This is currently unnecessary because every GUI1 widget is a TLD.
955  // They will draw themselves. Keeping code in case this changes.
956  return;
957 
958  //const rect clip = draw::get_clip();
959  //for(auto& btn : menu_buttons_) {
960  // if(clip.overlaps(btn->location())) {
961  // btn->set_dirty(true);
962  // btn->draw();
963  // }
964  //}
965 
966  //for(auto& btn : action_buttons_) {
967  // if(clip.overlaps(btn->location())) {
968  // btn->set_dirty(true);
969  // btn->draw();
970  // }
971  //}
972 }
973 
974 std::vector<texture> display::get_fog_shroud_images(const map_location& loc, image::TYPE image_type)
975 {
976  std::vector<std::string> names;
977  const auto adjacent = get_adjacent_tiles(loc);
978 
979  enum visibility { FOG = 0, SHROUD = 1, CLEAR = 2 };
980  std::array<visibility, 6> tiles;
981 
982  const std::array image_prefix{&game_config::fog_prefix, &game_config::shroud_prefix};
983 
984  for(int i = 0; i < 6; ++i) {
985  if(shrouded(adjacent[i])) {
986  tiles[i] = SHROUD;
987  } else if(!fogged(loc) && fogged(adjacent[i])) {
988  tiles[i] = FOG;
989  } else {
990  tiles[i] = CLEAR;
991  }
992  }
993 
994  for(int v = FOG; v != CLEAR; ++v) {
995  // Find somewhere that doesn't have overlap to use as a starting point
996  int start;
997  for(start = 0; start != 6; ++start) {
998  if(tiles[start] != v) {
999  break;
1000  }
1001  }
1002 
1003  if(start == 6) {
1004  // Completely surrounded by fog or shroud. This might have
1005  // a special graphic.
1006  const std::string name = *image_prefix[v] + "-all.png";
1007  if(image::exists(name)) {
1008  names.push_back(name);
1009  // Proceed to the next visibility (fog -> shroud -> clear).
1010  continue;
1011  }
1012  // No special graphic found. We'll just combine some other images
1013  // and hope it works out.
1014  start = 0;
1015  }
1016 
1017  // Find all the directions overlap occurs from
1018  for(int i = (start + 1) % 6, cap1 = 0; i != start && cap1 != 6; ++cap1) {
1019  if(tiles[i] == v) {
1020  std::ostringstream stream;
1021  std::string name;
1022  stream << *image_prefix[v];
1023 
1024  for(int cap2 = 0; v == tiles[i] && cap2 != 6; i = (i + 1) % 6, ++cap2) {
1025  stream << get_direction(i);
1026 
1027  if(!image::exists(stream.str() + ".png")) {
1028  // If we don't have any surface at all,
1029  // then move onto the next overlapped area
1030  if(name.empty()) {
1031  i = (i + 1) % 6;
1032  }
1033  break;
1034  } else {
1035  name = stream.str();
1036  }
1037  }
1038 
1039  if(!name.empty()) {
1040  names.push_back(name + ".png");
1041  }
1042  } else {
1043  i = (i + 1) % 6;
1044  }
1045  }
1046  }
1047 
1048  // now get the textures
1049  std::vector<texture> res;
1050 
1051  for(const std::string& name : names) {
1052  if(texture tex = image::get_texture(name, image_type)) {
1053  res.push_back(std::move(tex));
1054  }
1055  }
1056 
1057  return res;
1058 }
1059 
1060 void display::get_terrain_images(const map_location& loc, const std::string& timeid, TERRAIN_TYPE terrain_type)
1061 {
1062  terrain_image_vector_.clear();
1063 
1065  const time_of_day& tod = get_time_of_day(loc);
1066 
1067  // get all the light transitions
1068  const auto adjs = get_adjacent_tiles(loc);
1069  std::array<const time_of_day*, adjs.size()> atods;
1070 
1071  for(std::size_t d = 0; d < adjs.size(); ++d) {
1072  atods[d] = &get_time_of_day(adjs[d]);
1073  }
1074 
1075  for(int d = 0; d < 6; ++d) {
1076  /*
1077  concave
1078  _____
1079  / \
1080  / atod1 \_____
1081  \ !tod / \
1082  \_____/ atod2 \
1083  / \__\ !tod /
1084  / \_____/
1085  \ tod /
1086  \_____/
1087  */
1088 
1089  const time_of_day& atod1 = *atods[d];
1090  const time_of_day& atod2 = *atods[(d + 1) % 6];
1091 
1092  if(atod1.color == tod.color || atod2.color == tod.color || atod1.color != atod2.color) {
1093  continue;
1094  }
1095 
1096  if(lt.empty()) {
1097  // color the full hex before adding transitions
1098  tod_color col = tod.color + color_adjust_;
1099  lt = image::get_light_string(0, col.r, col.g, col.b);
1100  }
1101 
1102  // add the directional transitions
1103  tod_color acol = atod1.color + color_adjust_;
1104  lt += image::get_light_string(d + 1, acol.r, acol.g, acol.b);
1105  }
1106 
1107  for(int d = 0; d < 6; ++d) {
1108  /*
1109  convex 1
1110  _____
1111  / \
1112  / atod1 \_____
1113  \ !tod / \
1114  \_____/ atod2 \
1115  / \__\ tod /
1116  / \_____/
1117  \ tod /
1118  \_____/
1119  */
1120 
1121  const time_of_day& atod1 = *atods[d];
1122  const time_of_day& atod2 = *atods[(d + 1) % 6];
1123 
1124  if(atod1.color == tod.color || atod1.color == atod2.color) {
1125  continue;
1126  }
1127 
1128  if(lt.empty()) {
1129  // color the full hex before adding transitions
1130  tod_color col = tod.color + color_adjust_;
1131  lt = image::get_light_string(0, col.r, col.g, col.b);
1132  }
1133 
1134  // add the directional transitions
1135  tod_color acol = atod1.color + color_adjust_;
1136  lt += image::get_light_string(d + 7, acol.r, acol.g, acol.b);
1137  }
1138 
1139  for(int d = 0; d < 6; ++d) {
1140  /*
1141  convex 2
1142  _____
1143  / \
1144  / atod1 \_____
1145  \ tod / \
1146  \_____/ atod2 \
1147  / \__\ !tod /
1148  / \_____/
1149  \ tod /
1150  \_____/
1151  */
1152 
1153  const time_of_day& atod1 = *atods[d];
1154  const time_of_day& atod2 = *atods[(d + 1) % 6];
1155 
1156  if(atod2.color == tod.color || atod1.color == atod2.color) {
1157  continue;
1158  }
1159 
1160  if(lt.empty()) {
1161  // color the full hex before adding transitions
1162  tod_color col = tod.color + color_adjust_;
1163  lt = image::get_light_string(0, col.r, col.g, col.b);
1164  }
1165 
1166  // add the directional transitions
1167  tod_color acol = atod2.color + color_adjust_;
1168  lt += image::get_light_string(d + 13, acol.r, acol.g, acol.b);
1169  }
1170 
1171  if(lt.empty()){
1172  tod_color col = tod.color + color_adjust_;
1173  if(!col.is_zero()){
1174  // no real lightmap needed but still color the hex
1175  lt = image::get_light_string(-1, col.r, col.g, col.b);
1176  }
1177  }
1178 
1179  const terrain_builder::TERRAIN_TYPE builder_terrain_type = terrain_type == FOREGROUND
1182 
1183  if(const terrain_builder::imagelist* const terrains = builder_->get_terrain_at(loc, timeid, builder_terrain_type)) {
1184  // Cache the offmap name. Since it is themeable it can change, so don't make it static.
1185  const std::string off_map_name = "terrain/" + theme_.border().tile_image;
1186  for(const auto& terrain : *terrains) {
1187  const image::locator& image = animate_map_ ? terrain.get_current_frame() : terrain.get_first_frame();
1188 
1189  // We prevent ToD coloring and brightening of off-map tiles,
1190  // We need to test for the tile to be rendered and
1191  // not the location, since the transitions are rendered
1192  // over the offmap-terrain and these need a ToD coloring.
1193  texture tex;
1194  const bool off_map = (image.get_filename() == off_map_name
1195  || image.get_modifications().find("NO_TOD_SHIFT()") != std::string::npos);
1196 
1197  if(off_map) {
1198  tex = image::get_texture(image, image::HEXED);
1199  } else if(lt.empty()) {
1200  tex = image::get_texture(image, image::HEXED);
1201  } else {
1202  tex = image::get_lighted_texture(image, lt);
1203  }
1204 
1205  if(tex) {
1206  terrain_image_vector_.push_back(std::move(tex));
1207  }
1208  }
1209  }
1210 }
1211 
1213  const map_location& loc, const SDL_Rect& dest, const texture& tex)
1214 {
1215  drawing_buffer_.emplace_back(layer, loc, dest, tex);
1216  return drawing_buffer_.back();
1217 }
1218 
1220  const map_location& loc, const SDL_Rect& dest,
1221  const std::vector<texture> &tex)
1222 {
1223  drawing_buffer_.emplace_back(layer, loc, dest, tex);
1224  return drawing_buffer_.back();
1225 }
1226 
1227 enum {
1228  // you may adjust the following when needed:
1229 
1230  // maximum border. 3 should be safe even if a larger border is in use somewhere
1232 
1233  // store x, y, and layer in one 32 bit integer
1234  // 4 most significant bits == layer group => 16
1236 
1237  // 10 second most significant bits == y => 1024
1239 
1240  // 1 third most significant bit == x parity => 2
1242 
1243  // 8 fourth most significant bits == layer => 256
1245 
1246  // 9 least significant bits == x / 2 => 512 (really 1024 for x)
1248 };
1249 
1251  : key_(0)
1252 {
1253  // Start with the index of last group entry...
1254  unsigned int group_i = layer_groups.size() - 1;
1255 
1256  // ...and works backwards until the group containing the specified layer is found.
1257  while(layer < layer_groups[group_i]) {
1258  --group_i;
1259  }
1260 
1261  enum {
1262  SHIFT_LAYER = BITS_FOR_X_OVER_2,
1263  SHIFT_X_PARITY = BITS_FOR_LAYER + SHIFT_LAYER,
1264  SHIFT_Y = BITS_FOR_X_PARITY + SHIFT_X_PARITY,
1265  SHIFT_LAYER_GROUP = BITS_FOR_Y + SHIFT_Y
1266  };
1267  static_assert(SHIFT_LAYER_GROUP + BITS_FOR_LAYER_GROUP == sizeof(key_) * 8, "Bit field too small");
1268 
1269  // the parity of x must be more significant than the layer but less significant than y.
1270  // Thus basically every row is split in two: First the row containing all the odd x
1271  // then the row containing all the even x. Since thus the least significant bit of x is
1272  // not required for x ordering anymore it can be shifted out to the right.
1273  const unsigned int x_parity = static_cast<unsigned int>(loc.x) & 1;
1274  key_ = (group_i << SHIFT_LAYER_GROUP) | (static_cast<unsigned int>(loc.y + MAX_BORDER) << SHIFT_Y);
1275  key_ |= (x_parity << SHIFT_X_PARITY);
1276  key_ |= (static_cast<unsigned int>(layer) << SHIFT_LAYER) | static_cast<unsigned int>(loc.x + MAX_BORDER) / 2;
1277 }
1278 
1280 {
1281  // std::list::sort() is a stable sort
1282  drawing_buffer_.sort();
1283 
1284  auto clipper = draw::reduce_clip(map_area());
1285 
1286  /*
1287  * Info regarding the rendering algorithm.
1288  *
1289  * In order to render a hex properly it needs to be rendered per row. On
1290  * this row several layers need to be drawn at the same time. Mainly the
1291  * unit and the background terrain. This is needed since both can spill
1292  * in the next hex. The foreground terrain needs to be drawn before to
1293  * avoid decapitation a unit.
1294  *
1295  * This ended in the following priority order:
1296  * layergroup > location > layer > 'blit_helper' > surface
1297  */
1298 
1299  for(const blit_helper& blit : drawing_buffer_) {
1300  for(texture tex : blit.tex()) {
1301  if (blit.alpha_mod != SDL_ALPHA_OPAQUE) {
1302  tex.set_alpha_mod(blit.alpha_mod);
1303  }
1304  if (blit.r_mod != 255 || blit.g_mod != 255 || blit.b_mod != 255) {
1305  tex.set_color_mod(blit.r_mod, blit.g_mod, blit.b_mod);
1306  }
1307  draw::flipped(tex, blit.dest(), blit.hflip, blit.vflip);
1308  if (blit.highlight) {
1309  tex.set_blend_mode(SDL_BLENDMODE_ADD);
1310  tex.set_alpha_mod(blit.highlight);
1311  draw::flipped(tex, blit.dest(), blit.hflip, blit.vflip);
1312  tex.set_blend_mode(SDL_BLENDMODE_BLEND);
1313  }
1314  if (blit.r_mod != 255 || blit.g_mod != 255 || blit.b_mod != 255) {
1315  tex.set_color_mod(255, 255, 255);
1316  }
1317  if (blit.alpha_mod != SDL_ALPHA_OPAQUE || blit.highlight) {
1318  tex.set_alpha_mod(SDL_ALPHA_OPAQUE);
1319  }
1320  }
1321  }
1323 }
1324 
1326 {
1327  drawing_buffer_.clear();
1328 }
1329 
1331 {
1332  benchmark = !benchmark;
1333 }
1334 
1336 {
1337  debug_foreground = !debug_foreground;
1338 }
1339 
1340 // frametime is in milliseconds
1341 static unsigned calculate_fps(unsigned frametime)
1342 {
1343  return frametime != 0u ? 1000u / frametime : 999u;
1344 }
1345 
1347 {
1348  static int frames = 0;
1349  ++frames;
1350  const int sample_freq = 10;
1351 
1352  if(frames != sample_freq) {
1353  return;
1354  }
1355 
1356  const auto minmax_it = std::minmax_element(frametimes_.begin(), frametimes_.end());
1357  const unsigned render_avg = std::accumulate(frametimes_.begin(), frametimes_.end(), 0) / frametimes_.size();
1358  const int avg_fps = calculate_fps(render_avg);
1359  const int max_fps = calculate_fps(*minmax_it.first);
1360  const int min_fps = calculate_fps(*minmax_it.second);
1361  fps_history_.emplace_back(min_fps, avg_fps, max_fps);
1362  frames = 0;
1363 
1364  // flush out the stored fps values every so often
1365  if(fps_history_.size() == 1000) {
1366  std::string filename = filesystem::get_user_data_dir()+"/fps_log.csv";
1367  filesystem::scoped_ostream fps_log = filesystem::ostream_file(filename, std::ios_base::binary | std::ios_base::app);
1368  for(const auto& fps : fps_history_) {
1369  *fps_log << std::get<0>(fps) << "," << std::get<1>(fps) << "," << std::get<2>(fps) << "\n";
1370  }
1371  fps_history_.clear();
1372  }
1373 
1374  if(fps_handle_ != 0) {
1376  fps_handle_ = 0;
1377  }
1378  std::ostringstream stream;
1379  stream << "<tt> min/avg/max/act</tt>\n";
1380  stream << "<tt>FPS: " << std::setfill(' ') << std::setw(3) << min_fps << '/'<< std::setw(3) << avg_fps << '/' << std::setw(3) << max_fps << '/' << std::setw(3) << fps_actual_ << "</tt>\n";
1381  stream << "<tt>Time: " << std::setfill(' ') << std::setw(3) << *minmax_it.first << '/' << std::setw(3) << render_avg << '/' << std::setw(3) << *minmax_it.second << " ms</tt>\n";
1382  if (game_config::debug) {
1383  stream << "\nhex: " << drawn_hexes_*1.0/sample_freq;
1385  stream << " (" << (invalidated_hexes_-drawn_hexes_)*1.0/sample_freq << ")";
1386  }
1387  drawn_hexes_ = 0;
1388  invalidated_hexes_ = 0;
1389 
1390  font::floating_label flabel(stream.str());
1391  flabel.set_font_size(12);
1392  flabel.set_color(benchmark ? font::BAD_COLOR : font::NORMAL_COLOR);
1393  flabel.set_position(10, 100);
1394  flabel.set_alignment(font::LEFT_ALIGN);
1395 
1397 }
1398 
1400 {
1401  if(fps_handle_ != 0) {
1403  fps_handle_ = 0;
1404  drawn_hexes_ = 0;
1405  invalidated_hexes_ = 0;
1406  }
1407 }
1408 
1410 {
1411  // Most panels are transparent.
1412  if (panel.image().empty()) {
1413  return;
1414  }
1415 
1416  const rect& loc = panel.location(video::game_canvas());
1417 
1418  if (!loc.overlaps(draw::get_clip())) {
1419  return;
1420  }
1421 
1422  DBG_DP << "drawing panel " << panel.get_id() << ' ' << loc;
1423 
1424  texture tex(image::get_texture(panel.image()));
1425  if (!tex) {
1426  ERR_DP << "failed to load panel " << panel.get_id()
1427  << " texture: " << panel.image();
1428  return;
1429  }
1430 
1431  draw::tiled(tex, loc);
1432 }
1433 
1435 {
1436  const rect& loc = label.location(video::game_canvas());
1437 
1438  if (!loc.overlaps(draw::get_clip())) {
1439  return;
1440  }
1441 
1442  const std::string& text = label.text();
1443  const color_t text_color = label.font_rgb_set() ? label.font_rgb() : font::NORMAL_COLOR;
1444  const std::string& icon = label.icon();
1445 
1446  DBG_DP << "drawing label " << label.get_id() << ' ' << loc;
1447 
1448  if(icon.empty() == false) {
1449  draw::blit(image::get_texture(icon), loc);
1450 
1451  if(text.empty() == false) {
1452  tooltips::add_tooltip(loc,text);
1453  }
1454  } else if(text.empty() == false) {
1455  font::pango_draw_text(true, loc, label.font_size(),
1456  text_color, text, loc.x, loc.y
1457  );
1458  }
1459 }
1460 
1461 bool display::draw_all_panels(const rect& region)
1462 {
1463  bool drew = false;
1465 
1466  for(const auto& panel : theme_.panels()) {
1467  if(region.overlaps(panel.location(game_canvas))) {
1468  draw_panel(panel);
1469  drew = true;
1470  }
1471  }
1472 
1473  for(const auto& label : theme_.labels()) {
1474  if(region.overlaps(label.location(game_canvas))) {
1475  draw_label(label);
1476  drew = true;
1477  }
1478  }
1479 
1480  return drew;
1481 }
1482 
1484  const drawing_layer layer,
1485  const std::string& text,
1486  std::size_t font_size,
1487  color_t color,
1488  double x_in_hex,
1489  double y_in_hex)
1490 {
1491  if (text.empty()) return;
1492 
1493  const double zf = get_zoom_factor();
1494  const int font_sz = int(font_size * zf);
1495 
1496  // TODO: highdpi - better outline
1497  texture text_surf = font::pango_render_text(text, font_sz, color);
1498  const int x = get_location_x(loc) - text_surf.w()/2
1499  + static_cast<int>(x_in_hex* hex_size());
1500  const int y = get_location_y(loc) - text_surf.h()/2
1501  + static_cast<int>(y_in_hex* hex_size());
1502  const int w = text_surf.w();
1503  const int h = text_surf.h();
1504  for (int dy=-1; dy <= 1; ++dy) {
1505  for (int dx=-1; dx <= 1; ++dx) {
1506  if (dx!=0 || dy!=0) {
1507  const SDL_Rect dest{int(x + dx*zf), int(y + dy*zf), w, h};
1508  drawing_buffer_add(layer, loc, dest, text_surf)
1509  .set_color_and_alpha({0, 0, 0, 128});
1510  }
1511  }
1512  }
1513  drawing_buffer_add(layer, loc, {x, y, w, h}, text_surf);
1514 }
1515 
1517  std::string& image_path,
1518  int image_height,
1519  double submersion_amount,
1520  int shift = 0)
1521 {
1522  // We may also want to shift the position so that the waterline matches.
1523  // Note: This currently has blending problems (see the note on sdl_blit),
1524  // but if that blending problem is fixed it should work.
1525  if (shift) {
1526  image_path = "misc/blank-hex.png~BLIT(" + image_path;
1527  }
1528 
1529  // general formula for submerge alpha:
1530  // if (y > WATERLINE)
1531  // then min(max(alpha_mod, 0), 1) * alpha
1532  // else alpha
1533  // where alpha_mod = alpha_base - (y - WATERLINE) * alpha_delta
1534  // formula variables: x, y, red, green, blue, alpha, width, height
1535  // full WFL string:
1536  // "~ADJUST_ALPHA(if(y>DL,clamp((AB-(y-DL)*AD),0,1)*alpha,alpha))"
1537  // where DL = submersion line in pixels from top of image
1538  // AB = base alpha proportion at submersion line (30%)
1539  // AD = proportional alpha delta per pixel (1.5%)
1540  if (submersion_amount > 0.0) {
1541  int submersion_line = image_height * (1.0 - submersion_amount);
1542  const std::string sl_string = std::to_string(submersion_line);
1543  image_path += "~ADJUST_ALPHA(if(y>";
1544  image_path += sl_string;
1545  image_path += ",clamp((0.3-(y-";
1546  image_path += sl_string;
1547  image_path += ")*0.015),0,1)*alpha,alpha))";
1548  }
1549  // FUTURE: this submerge function could be done using SDL_RenderGeometry,
1550  // but that's not available below SDL 2.0.18.
1551  // Alternately it could also be done fairly easily using shaders,
1552  // if a graphics system supporting them (such as openGL) is moved to.
1553 
1554  if (shift) {
1555  // Finish the shifting blit. This assumes a hex-sized image.
1556  image_path += ",0,";
1557  image_path += std::to_string(shift);
1558  image_path += ')';
1559  }
1560 }
1561 
1563  const map_location& loc, const image::locator& i_locator,
1564  bool hreverse, bool greyscale, uint8_t alpha, double highlight,
1565  color_t blendto, double blend_ratio, double submerge, bool vreverse)
1566 {
1567  if (alpha == 0) {
1568  return;
1569  }
1570 
1571  const point image_size = image::get_size(i_locator);
1572  if (!image_size.x || !image_size.y) {
1573  return;
1574  }
1575 
1576  rect dest = scaled_to_zoom({x, y, image_size.x, image_size.y});
1577  if (!dest.overlaps(map_area())) {
1578  return;
1579  }
1580 
1581  // For now, we add to the existing IPF modifications for the image.
1582  std::string new_modifications;
1583 
1584  if (greyscale) {
1585  new_modifications += "~GS()";
1586  }
1587 
1588  add_submerge_ipf_mod(new_modifications, image_size.y, submerge);
1589 
1590  texture tex;
1591  if (!new_modifications.empty()) {
1592  const image::locator modified_locator(
1593  i_locator.get_filename(),
1594  i_locator.get_modifications() + new_modifications
1595  );
1596  tex = image::get_texture(modified_locator);
1597  } else {
1598  tex = image::get_texture(i_locator);
1599  }
1600 
1601  // Clamp blend ratio so nothing weird happens
1602  blend_ratio = std::clamp(blend_ratio, 0.0, 1.0);
1603 
1604  blit_helper& bh = drawing_buffer_add(drawing_layer, loc, dest, tex);
1605  bh.hflip = hreverse;
1606  bh.vflip = vreverse;
1607  bh.alpha_mod = alpha;
1608  bh.highlight = float_to_color(highlight);
1609 
1610  // SDL hax to apply an active washout tint at the correct ratio
1611  if (blend_ratio > 0.0) {
1612  // Get a pure-white version of the texture
1613  const image::locator whiteout_locator(
1614  i_locator.get_filename(),
1615  i_locator.get_modifications()
1616  + new_modifications
1617  + "~CHAN(255, 255, 255, alpha)"
1618  );
1619  const texture& wt = image::get_texture(whiteout_locator);
1620  // Blit the pure white version, tinted to the right colour
1621  blit_helper &wh = drawing_buffer_add(drawing_layer, loc, dest, wt);
1622  wh.hflip = hreverse;
1623  wh.vflip = vreverse;
1624  wh.alpha_mod = uint8_t(alpha * blend_ratio);
1625  wh.r_mod = blendto.r;
1626  wh.g_mod = blendto.g;
1627  wh.b_mod = blendto.b;
1628  }
1629 }
1630 
1632 {
1634  selectedHex_ = hex;
1637 }
1638 
1640 {
1641  if(mouseoverHex_ == hex) {
1642  return;
1643  }
1645  mouseoverHex_ = hex;
1647 }
1648 
1649 void display::set_diagnostic(const std::string& msg)
1650 {
1651  if(diagnostic_label_ != 0) {
1653  diagnostic_label_ = 0;
1654  }
1655 
1656  if(!msg.empty()) {
1657  font::floating_label flabel(msg);
1659  flabel.set_color(font::YELLOW_COLOR);
1660  flabel.set_position(300, 50);
1661  flabel.set_clip_rect(map_outside_area());
1662 
1664  }
1665 }
1666 
1668 {
1669  static int time_between_draws = preferences::draw_delay();
1670  if(time_between_draws < 0) {
1671  time_between_draws = 1000 / video::current_refresh_rate();
1672  }
1673 
1674  frametimes_.push_back(SDL_GetTicks() - last_frame_finished_);
1675  fps_counter_++;
1676  using std::chrono::duration_cast;
1677  using std::chrono::seconds;
1678  using std::chrono::steady_clock;
1679  const seconds current_second = duration_cast<seconds>(steady_clock::now().time_since_epoch());
1680  if(current_second != fps_start_) {
1681  fps_start_ = current_second;
1683  fps_counter_ = 0;
1684  }
1685 
1686  last_frame_finished_ = SDL_GetTicks();
1687 }
1688 
1690 {
1691  for(auto i = action_buttons_.begin(); i != action_buttons_.end(); ++i) {
1692  if((*i)->pressed()) {
1693  const std::size_t index = std::distance(action_buttons_.begin(), i);
1694  if(index >= theme_.actions().size()) {
1695  assert(false);
1696  return nullptr;
1697  }
1698  return &theme_.actions()[index];
1699  }
1700  }
1701 
1702  return nullptr;
1703 }
1704 
1706 {
1707  for(auto i = menu_buttons_.begin(); i != menu_buttons_.end(); ++i) {
1708  if((*i)->pressed()) {
1709  const std::size_t index = std::distance(menu_buttons_.begin(), i);
1710  if(index >= theme_.menus().size()) {
1711  assert(false);
1712  return nullptr;
1713  }
1714  return theme_.get_menu_item((*i)->id());
1715  }
1716  }
1717 
1718  return nullptr;
1719 }
1720 
1721 void display::announce(const std::string& message, const color_t& color, const announce_options& options)
1722 {
1723  if(options.discard_previous) {
1724  font::remove_floating_label(prevLabel);
1725  }
1726  font::floating_label flabel(message);
1728  flabel.set_color(color);
1729  flabel.set_position(
1731  flabel.set_lifetime(options.lifetime);
1732  flabel.set_clip_rect(map_outside_area());
1733 
1734  prevLabel = font::add_floating_label(flabel);
1735 }
1736 
1738 {
1739  if(video::headless()) {
1740  return;
1741  }
1742 
1743  const rect& area = minimap_area();
1744  if(area.empty()){
1745  return;
1746  }
1747 
1749  area.w, area.h, get_map(),
1750  dc_->teams().empty() ? nullptr : &dc_->teams()[currentTeam_],
1751  (selectedHex_.valid() && !is_blindfolded()) ? &reach_map_ : nullptr
1752  ));
1753 
1754  redraw_minimap();
1755 }
1756 
1758 {
1760 }
1761 
1763 {
1764  const rect& area = minimap_area();
1765 
1766  if(area.empty() || !area.overlaps(draw::get_clip())) {
1767  return;
1768  }
1769 
1770  if(!minimap_) {
1771  ERR_DP << "trying to draw null minimap";
1772  return;
1773  }
1774 
1775  auto clipper = draw::reduce_clip(area);
1776 
1777  // Draw the minimap background.
1778  draw::fill(area, 31, 31, 23);
1779 
1780  // Update the minimap location for mouse and units functions
1781  minimap_location_.x = area.x + (area.w - minimap_.w()) / 2;
1782  minimap_location_.y = area.y + (area.h - minimap_.h()) / 2;
1785 
1786  // Draw the minimap.
1787  if (minimap_) {
1789  }
1790 
1792 
1793  // calculate the visible portion of the map:
1794  // scaling between minimap and full map images
1795  double xscaling = 1.0*minimap_.w() / (get_map().w()*hex_width());
1796  double yscaling = 1.0*minimap_.h() / (get_map().h()*hex_size());
1797 
1798  // we need to shift with the border size
1799  // and the 0.25 from the minimap balanced drawing
1800  // and the possible difference between real map and outside off-map
1801  SDL_Rect map_rect = map_area();
1802  SDL_Rect map_out_rect = map_outside_area();
1803  double border = theme_.border().size;
1804  double shift_x = -border * hex_width() - (map_out_rect.w - map_rect.w) / 2;
1805  double shift_y = -(border + 0.25) * hex_size() - (map_out_rect.h - map_rect.h) / 2;
1806 
1807  int view_x = static_cast<int>((xpos_ + shift_x) * xscaling);
1808  int view_y = static_cast<int>((ypos_ + shift_y) * yscaling);
1809  int view_w = static_cast<int>(map_out_rect.w * xscaling);
1810  int view_h = static_cast<int>(map_out_rect.h * yscaling);
1811 
1812  SDL_Rect outline_rect {
1813  minimap_location_.x + view_x - 1,
1814  minimap_location_.y + view_y - 1,
1815  view_w + 2,
1816  view_h + 2
1817  };
1818 
1819  draw::rect(outline_rect, 255, 255, 255);
1820 }
1821 
1823 {
1824  if (!preferences::minimap_draw_units() || is_blindfolded()) return;
1825 
1826  double xscaling = 1.0 * minimap_location_.w / get_map().w();
1827  double yscaling = 1.0 * minimap_location_.h / get_map().h();
1828 
1829  for(const auto& u : dc_->units()) {
1830  if (fogged(u.get_location()) ||
1831  (dc_->teams()[currentTeam_].is_enemy(u.side()) &&
1832  u.invisible(u.get_location())) ||
1833  u.get_hidden()) {
1834  continue;
1835  }
1836 
1837  int side = u.side();
1838  color_t col = team::get_minimap_color(side);
1839 
1841  auto status = orb_status::allied;
1842  if(dc_->teams()[currentTeam_].is_enemy(side)) {
1843  status = orb_status::enemy;
1844  } else if(currentTeam_ + 1 == static_cast<unsigned>(side)) {
1845  status = dc_->unit_orb_status(u);
1846  } else {
1847  // no-op, status is already set to orb_status::allied;
1848  }
1850  }
1851 
1852  double u_x = u.get_location().x * xscaling;
1853  double u_y = (u.get_location().y + (is_odd(u.get_location().x) ? 1 : -1)/4.0) * yscaling;
1854  // use 4/3 to compensate the horizontal hexes imbrication
1855  double u_w = 4.0 / 3.0 * xscaling;
1856  double u_h = yscaling;
1857 
1858  SDL_Rect r {
1859  minimap_location_.x + int(std::round(u_x))
1860  , minimap_location_.y + int(std::round(u_y))
1861  , int(std::round(u_w))
1862  , int(std::round(u_h))
1863  };
1864 
1865  draw::fill(r, col.r, col.g, col.b, col.a);
1866  }
1867 }
1868 
1869 bool display::scroll(int xmove, int ymove, bool force)
1870 {
1871  if(view_locked_ && !force) {
1872  return false;
1873  }
1874 
1875  // No move offset, do nothing.
1876  if(xmove == 0 && ymove == 0) {
1877  return false;
1878  }
1879 
1880  int new_x = xpos_ + xmove;
1881  int new_y = ypos_ + ymove;
1882 
1883  bounds_check_position(new_x, new_y);
1884 
1885  // Camera position doesn't change, exit.
1886  if(xpos_ == new_x && ypos_ == new_y) {
1887  return false;
1888  }
1889 
1890  const int diff_x = xpos_ - new_x;
1891  const int diff_y = ypos_ - new_y;
1892 
1893  xpos_ = new_x;
1894  ypos_ = new_y;
1895 
1896  /* Adjust floating label positions. This only affects labels whose position is anchored
1897  * to the map instead of the screen. In order to do that, we want to adjust their drawing
1898  * coordinates in the opposite direction of the screen scroll.
1899  *
1900  * The check a few lines up prevents any scrolling from happening if the camera position
1901  * doesn't change. Without that, the label still scroll even when the map edge is reached.
1902  * If that's removed, the following formula should work instead:
1903  *
1904  * const int label_[x,y]_adjust = [x,y]pos_ - new_[x,y];
1905  */
1906  font::scroll_floating_labels(diff_x, diff_y);
1907 
1909 
1910  //
1911  // NOTE: the next three blocks can be removed once we switch to accelerated rendering.
1912  //
1913 
1914  if(!video::headless()) {
1915  rect dst = map_area();
1916  dst.x += diff_x;
1917  dst.y += diff_y;
1918  dst.clip(map_area());
1919 
1920  rect src = dst;
1921  src.x -= diff_x;
1922  src.y -= diff_y;
1923 
1924  // swap buffers
1926 
1927  // Set the source region to blit from
1928  back_.set_src(src);
1929 
1930  // copy from the back to the front buffer
1931  auto rts = draw::set_render_target(front_);
1932  draw::blit(back_, dst);
1933 
1934  back_.clear_src();
1935 
1936  // queue repaint
1938  }
1939 
1940  if(diff_y != 0) {
1941  SDL_Rect r = map_area();
1942 
1943  if(diff_y < 0) {
1944  r.y = r.y + r.h + diff_y;
1945  }
1946 
1947  r.h = std::abs(diff_y);
1949  }
1950 
1951  if(diff_x != 0) {
1952  SDL_Rect r = map_area();
1953 
1954  if(diff_x < 0) {
1955  r.x = r.x + r.w + diff_x;
1956  }
1957 
1958  r.w = std::abs(diff_x);
1960  }
1961 
1963 
1964  redraw_minimap();
1965 
1966  return true;
1967 }
1968 
1970 {
1971  return zoom_ == MaxZoom;
1972 }
1973 
1975 {
1976  return zoom_ == MinZoom;
1977 }
1978 
1979 bool display::set_zoom(bool increase)
1980 {
1981  // Ensure we don't try to access nonexistent vector indices.
1982  zoom_index_ = std::clamp(increase ? zoom_index_ + 1 : zoom_index_ - 1, 0, final_zoom_index);
1983 
1984  // No validation check is needed in the next step since we've already set the index here and
1985  // know the new zoom value is indeed valid.
1986  return set_zoom(zoom_levels[zoom_index_], false);
1987 }
1988 
1989 bool display::set_zoom(unsigned int amount, const bool validate_value_and_set_index)
1990 {
1991  unsigned int new_zoom = std::clamp(amount, MinZoom, MaxZoom);
1992 
1993  LOG_DP << "new_zoom = " << new_zoom;
1994 
1995  if(new_zoom == zoom_) {
1996  return false;
1997  }
1998 
1999  if(validate_value_and_set_index) {
2000  zoom_index_ = get_zoom_levels_index (new_zoom);
2001  new_zoom = zoom_levels[zoom_index_];
2002  }
2003 
2004  const SDL_Rect& outside_area = map_outside_area();
2005  const SDL_Rect& area = map_area();
2006 
2007  // Turn the zoom factor to a double in order to avoid rounding errors.
2008  double zoom_factor = static_cast<double>(new_zoom) / static_cast<double>(zoom_);
2009 
2010  // INVARIANT: xpos_ + area.w == xend where xend is as in bounds_check_position()
2011  //
2012  // xpos_: Position of the leftmost visible map pixel of the viewport, in pixels.
2013  // Affected by the current zoom: this->zoom_ pixels to the hex.
2014  //
2015  // xpos_ + area.w/2: Position of the center of the viewport, in pixels.
2016  //
2017  // (xpos_ + area.w/2) * new_zoom/zoom_: Position of the center of the
2018  // viewport, as it would be under new_zoom.
2019  //
2020  // (xpos_ + area.w/2) * new_zoom/zoom_ - area.w/2: Position of the
2021  // leftmost visible map pixel, as it would be under new_zoom.
2022  xpos_ = std::round(((xpos_ + area.w / 2) * zoom_factor) - (area.w / 2));
2023  ypos_ = std::round(((ypos_ + area.h / 2) * zoom_factor) - (area.h / 2));
2024  xpos_ -= (outside_area.w - area.w) / 2;
2025  ypos_ -= (outside_area.h - area.h) / 2;
2026 
2027  zoom_ = new_zoom;
2029  if(zoom_ != DefaultZoom) {
2030  last_zoom_ = zoom_;
2031  }
2032 
2035 
2037  redraw_background_ = true;
2038  invalidate_all();
2039 
2040  return true;
2041 }
2042 
2044 {
2045  if (zoom_ != DefaultZoom) {
2046  last_zoom_ = zoom_;
2048  } else {
2049  // When we are already at the default zoom,
2050  // switch to the last zoom used
2052  }
2053 }
2054 
2056 {
2057  int x = get_location_x(loc);
2058  int y = get_location_y(loc);
2059  return !outside_area(map_area(), x, y);
2060 }
2061 
2063 {
2064  int x = get_location_x(loc);
2065  int y = get_location_y(loc);
2066  const SDL_Rect &area = map_area();
2067  int hw = hex_width(), hs = hex_size();
2068  return x + hs >= area.x - hw && x < area.x + area.w + hw &&
2069  y + hs >= area.y - hs && y < area.y + area.h + hs;
2070 }
2071 
2072 void display::scroll_to_xy(int screenxpos, int screenypos, SCROLL_TYPE scroll_type, bool force)
2073 {
2074  if(!force && (view_locked_ || !preferences::scroll_to_action())) return;
2075  if(video::headless()) {
2076  return;
2077  }
2078  const SDL_Rect area = map_area();
2079  const int xmove_expected = screenxpos - (area.x + area.w/2);
2080  const int ymove_expected = screenypos - (area.y + area.h/2);
2081 
2082  int xpos = xpos_ + xmove_expected;
2083  int ypos = ypos_ + ymove_expected;
2084  bounds_check_position(xpos, ypos);
2085  int xmove = xpos - xpos_;
2086  int ymove = ypos - ypos_;
2087 
2088  if(scroll_type == WARP || scroll_type == ONSCREEN_WARP || turbo_speed() > 2.0 || preferences::scroll_speed() > 99) {
2089  scroll(xmove,ymove,true);
2090  redraw_minimap();
2091  events::draw();
2092  return;
2093  }
2094 
2095  // Doing an animated scroll, with acceleration etc.
2096 
2097  int x_old = 0;
2098  int y_old = 0;
2099 
2100  const double dist_total = std::hypot(xmove, ymove);
2101  double dist_moved = 0.0;
2102 
2103  int t_prev = SDL_GetTicks();
2104 
2105  double velocity = 0.0;
2106  while (dist_moved < dist_total) {
2107  events::pump();
2108 
2109  int t = SDL_GetTicks();
2110  double dt = (t - t_prev) / 1000.0;
2111  if (dt > 0.200) {
2112  // Do not skip too many frames on slow PCs
2113  dt = 0.200;
2114  }
2115  t_prev = t;
2116 
2117  const double accel_time = 0.3 / turbo_speed(); // seconds until full speed is reached
2118  const double decel_time = 0.4 / turbo_speed(); // seconds from full speed to stop
2119 
2120  double velocity_max = preferences::scroll_speed() * 60.0;
2121  velocity_max *= turbo_speed();
2122  double accel = velocity_max / accel_time;
2123  double decel = velocity_max / decel_time;
2124 
2125  // If we started to decelerate now, where would we stop?
2126  double stop_time = velocity / decel;
2127  double dist_stop = dist_moved + velocity*stop_time - 0.5*decel*stop_time*stop_time;
2128  if (dist_stop > dist_total || velocity > velocity_max) {
2129  velocity -= decel * dt;
2130  if (velocity < 1.0) velocity = 1.0;
2131  } else {
2132  velocity += accel * dt;
2133  if (velocity > velocity_max) velocity = velocity_max;
2134  }
2135 
2136  dist_moved += velocity * dt;
2137  if (dist_moved > dist_total) dist_moved = dist_total;
2138 
2139  int x_new = std::round(xmove * dist_moved / dist_total);
2140  int y_new = std::round(ymove * dist_moved / dist_total);
2141 
2142  int dx = x_new - x_old;
2143  int dy = y_new - y_old;
2144 
2145  scroll(dx,dy,true);
2146  x_old += dx;
2147  y_old += dy;
2148 
2149  redraw_minimap();
2150  events::draw();
2151  }
2152 }
2153 
2154 void display::scroll_to_tile(const map_location& loc, SCROLL_TYPE scroll_type, bool check_fogged, bool force)
2155 {
2156  if(get_map().on_board(loc) == false) {
2157  ERR_DP << "Tile at " << loc << " isn't on the map, can't scroll to the tile.";
2158  return;
2159  }
2160 
2161  std::vector<map_location> locs;
2162  locs.push_back(loc);
2163  scroll_to_tiles(locs, scroll_type, check_fogged,false,0.0,force);
2164 }
2165 
2167  SCROLL_TYPE scroll_type, bool check_fogged,
2168  double add_spacing, bool force)
2169 {
2170  std::vector<map_location> locs;
2171  locs.push_back(loc1);
2172  locs.push_back(loc2);
2173  scroll_to_tiles(locs, scroll_type, check_fogged, false, add_spacing,force);
2174 }
2175 
2176 void display::scroll_to_tiles(const std::vector<map_location>::const_iterator & begin,
2177  const std::vector<map_location>::const_iterator & end,
2178  SCROLL_TYPE scroll_type, bool check_fogged,
2179  bool only_if_possible, double add_spacing, bool force)
2180 {
2181  // basically we calculate the min/max coordinates we want to have on-screen
2182  int minx = 0;
2183  int maxx = 0;
2184  int miny = 0;
2185  int maxy = 0;
2186  bool valid = false;
2187 
2188  for(std::vector<map_location>::const_iterator itor = begin; itor != end ; ++itor) {
2189  if(get_map().on_board(*itor) == false) continue;
2190  if(check_fogged && fogged(*itor)) continue;
2191 
2192  int x = get_location_x(*itor);
2193  int y = get_location_y(*itor);
2194 
2195  if (!valid) {
2196  minx = x;
2197  maxx = x;
2198  miny = y;
2199  maxy = y;
2200  valid = true;
2201  } else {
2202  int minx_new = std::min<int>(minx,x);
2203  int miny_new = std::min<int>(miny,y);
2204  int maxx_new = std::max<int>(maxx,x);
2205  int maxy_new = std::max<int>(maxy,y);
2206  SDL_Rect r = map_area();
2207  r.x = minx_new;
2208  r.y = miny_new;
2209  if(outside_area(r, maxx_new, maxy_new)) {
2210  // we cannot fit all locations to the screen
2211  if (only_if_possible) return;
2212  break;
2213  }
2214  minx = minx_new;
2215  miny = miny_new;
2216  maxx = maxx_new;
2217  maxy = maxy_new;
2218  }
2219  }
2220  //if everything is fogged or the location list is empty
2221  if(!valid) return;
2222 
2223  if (scroll_type == ONSCREEN || scroll_type == ONSCREEN_WARP) {
2224  SDL_Rect r = map_area();
2225  int spacing = std::round(add_spacing * hex_size());
2226  r.x += spacing;
2227  r.y += spacing;
2228  r.w -= 2*spacing;
2229  r.h -= 2*spacing;
2230  if (!outside_area(r, minx,miny) && !outside_area(r, maxx,maxy)) {
2231  return;
2232  }
2233  }
2234 
2235  // let's do "normal" rectangle math from now on
2236  SDL_Rect locs_bbox;
2237  locs_bbox.x = minx;
2238  locs_bbox.y = miny;
2239  locs_bbox.w = maxx - minx + hex_size();
2240  locs_bbox.h = maxy - miny + hex_size();
2241 
2242  // target the center
2243  int target_x = locs_bbox.x + locs_bbox.w/2;
2244  int target_y = locs_bbox.y + locs_bbox.h/2;
2245 
2246  if (scroll_type == ONSCREEN || scroll_type == ONSCREEN_WARP) {
2247  // when doing an ONSCREEN scroll we do not center the target unless needed
2248  SDL_Rect r = map_area();
2249  int map_center_x = r.x + r.w/2;
2250  int map_center_y = r.y + r.h/2;
2251 
2252  int h = r.h;
2253  int w = r.w;
2254 
2255  // we do not want to be only inside the screen rect, but center a bit more
2256  double inside_frac = 0.5; // 0.0 = always center the target, 1.0 = scroll the minimum distance
2257  w = static_cast<int>(w * inside_frac);
2258  h = static_cast<int>(h * inside_frac);
2259 
2260  // shrink the rectangle by the size of the locations rectangle we found
2261  // such that the new task to fit a point into a rectangle instead of rectangle into rectangle
2262  w -= locs_bbox.w;
2263  h -= locs_bbox.h;
2264 
2265  if (w < 1) w = 1;
2266  if (h < 1) h = 1;
2267 
2268  r.x = target_x - w/2;
2269  r.y = target_y - h/2;
2270  r.w = w;
2271  r.h = h;
2272 
2273  // now any point within r is a possible target to scroll to
2274  // we take the one with the minimum distance to map_center
2275  // which will always be at the border of r
2276 
2277  if (map_center_x < r.x) {
2278  target_x = r.x;
2279  target_y = map_center_y;
2280  if (target_y < r.y) target_y = r.y;
2281  if (target_y > r.y+r.h-1) target_y = r.y+r.h-1;
2282  } else if (map_center_x > r.x+r.w-1) {
2283  target_x = r.x+r.w-1;
2284  target_y = map_center_y;
2285  if (target_y < r.y) target_y = r.y;
2286  if (target_y >= r.y+r.h) target_y = r.y+r.h-1;
2287  } else if (map_center_y < r.y) {
2288  target_y = r.y;
2289  target_x = map_center_x;
2290  if (target_x < r.x) target_x = r.x;
2291  if (target_x > r.x+r.w-1) target_x = r.x+r.w-1;
2292  } else if (map_center_y > r.y+r.h-1) {
2293  target_y = r.y+r.h-1;
2294  target_x = map_center_x;
2295  if (target_x < r.x) target_x = r.x;
2296  if (target_x > r.x+r.w-1) target_x = r.x+r.w-1;
2297  } else {
2298  ERR_DP << "Bug in the scrolling code? Looks like we would not need to scroll after all...";
2299  // keep the target at the center
2300  }
2301  }
2302 
2303  scroll_to_xy(target_x, target_y,scroll_type,force);
2304 }
2305 
2306 
2308 {
2309  const unsigned int orig_zoom = zoom_;
2310 
2311  if(zoom_ < MinZoom) {
2312  zoom_ = MinZoom;
2313  }
2314 
2315  if(zoom_ > MaxZoom) {
2316  zoom_ = MaxZoom;
2317  }
2318 
2320 
2321  if(zoom_ != orig_zoom) {
2323  }
2324 }
2325 
2326 void display::bounds_check_position(int& xpos, int& ypos) const
2327 {
2328  const int tile_width = hex_width();
2329 
2330  // Adjust for the border 2 times
2331  const int xend = static_cast<int>(tile_width * (get_map().w() + 2 * theme_.border().size) + tile_width/3);
2332  const int yend = static_cast<int>(zoom_ * (get_map().h() + 2 * theme_.border().size) + zoom_/2);
2333 
2334  if(xpos > xend - map_area().w) {
2335  xpos = xend - map_area().w;
2336  }
2337 
2338  if(ypos > yend - map_area().h) {
2339  ypos = yend - map_area().h;
2340  }
2341 
2342  if(xpos < 0) {
2343  xpos = 0;
2344  }
2345 
2346  if(ypos < 0) {
2347  ypos = 0;
2348  }
2349 }
2350 
2351 double display::turbo_speed() const
2352 {
2353  bool res = preferences::turbo();
2354  if(keys_[SDLK_LSHIFT] || keys_[SDLK_RSHIFT]) {
2355  res = !res;
2356  }
2357 
2358  res |= video::headless();
2359  if(res)
2360  return preferences::turbo_speed();
2361  else
2362  return 1.0;
2363 }
2364 
2366  const std::string& old_mask,
2367  const std::string& new_mask)
2368 {
2369  // TODO: hwaccel - this needs testing as it's not used in mainline
2372 
2373  int duration = 300 / turbo_speed();
2374  int start = SDL_GetTicks();
2375  for(int now = start; now < start + duration; now = SDL_GetTicks()) {
2376  float prop_f = float(now - start) / float(duration);
2377  uint8_t p = float_to_color(prop_f);
2378  tod_hex_alpha2 = p;
2379  tod_hex_alpha1 = ~p;
2382  }
2383 
2384  tod_hex_mask1.reset();
2385  tod_hex_mask2.reset();
2386 }
2387 
2388 void display::fade_to(const color_t& c, int duration)
2389 {
2390  uint32_t start = SDL_GetTicks();
2391  color_t fade_start = fade_color_;
2392  color_t fade_end = c;
2393 
2394  // If we started transparent, assume the same colour
2395  if(fade_start.a == 0) {
2396  fade_start.r = fade_end.r;
2397  fade_start.g = fade_end.g;
2398  fade_start.b = fade_end.b;
2399  }
2400 
2401  // If we are ending transparent, assume the same colour
2402  if(fade_end.a == 0) {
2403  fade_end.r = fade_start.r;
2404  fade_end.g = fade_start.g;
2405  fade_end.b = fade_start.b;
2406  }
2407 
2408  // Smoothly blend and display
2409  for(uint32_t now = start; now < start + duration; now = SDL_GetTicks()) {
2410  float prop_f = float(now - start) / float(duration);
2411  uint8_t p = float_to_color(prop_f);
2412  fade_color_ = fade_start.smooth_blend(fade_end, p);
2415  }
2416  fade_color_ = fade_end;
2418  events::draw();
2419 }
2420 
2422 {
2423  fade_color_ = c;
2424 }
2425 
2427 {
2428  if(video::headless())
2429  return;
2430 
2431  DBG_DP << "redrawing everything";
2432 
2433  // This is specifically for game_display.
2434  // It would probably be better to simply make this function virtual,
2435  // if game_display needs to do special processing.
2436  invalidateGameStatus_ = true;
2437 
2438  reportLocations_.clear();
2439  reportSurfaces_.clear();
2440  reports_.clear();
2441 
2443 
2445 
2447 
2448  if(!menu_buttons_.empty() || !action_buttons_.empty()) {
2449  create_buttons();
2450  }
2451 
2452  if(resources::controller) {
2454  if(command_executor != nullptr) {
2455  // This function adds button overlays,
2456  // it needs to be run after recreating the buttons.
2457  command_executor->set_button_state();
2458  }
2459  }
2460 
2461  if (!gui::in_dialog()) {
2463  }
2464 
2465  redraw_background_ = true;
2466 
2467  // This is only for one specific use, which is by the editor controller.
2468  // It would be vastly better if this didn't exist.
2469  for(std::function<void(display&)> f : redraw_observers_) {
2470  f(*this);
2471  }
2472 
2473  invalidate_all();
2474 
2476 }
2477 
2479 {
2480  // Could redraw a smaller region if the display doesn't use it all,
2481  // but when does that ever happen?
2483 }
2484 
2485 void display::add_redraw_observer(std::function<void(display&)> f)
2486 {
2487  redraw_observers_.push_back(f);
2488 }
2489 
2491 {
2492  redraw_observers_.clear();
2493 }
2494 
2496 {
2497  if(video::headless()) {
2498  DBG_DP << "display::draw denied";
2499  return;
2500  }
2501  //DBG_DP << "display::draw";
2502 
2503  // I have no idea why this is messing with sync context,
2504  // but i'm not going to touch it.
2506 
2507  // This isn't the best, but also isn't important enough to do better.
2508  if(redraw_background_) {
2509  DBG_DP << "display::draw redraw background";
2512  redraw_background_ = false;
2513  }
2514 
2515  if(!get_map().empty()) {
2516  if(!invalidated_.empty()) {
2517  draw_invalidated();
2518  invalidated_.clear();
2519  }
2521  }
2522 
2523  if(preferences::show_fps() || benchmark) {
2524  update_fps_label();
2525  update_fps_count();
2526  } else if(fps_handle_ != 0) {
2527  clear_fps_label();
2528  }
2529 }
2530 
2532 {
2533  //DBG_DP << "display::update";
2534  // Ensure render textures are correctly sized and up-to-date.
2536 
2537  // Trigger cache rebuild if animated water preference has changed.
2540  builder_->rebuild_cache_all();
2541  }
2542 
2543  if(benchmark) {
2544  invalidate_all();
2545  }
2546 }
2547 
2549 {
2550  //DBG_DP << "display::layout";
2551 
2552  // There's nothing that actually does layout here, it all happens in
2553  // response to events. This isn't ideal, but neither is changing that.
2554 
2555  // Post-layout / Pre-render
2556 
2557  if (!get_map().empty()) {
2558  if(redraw_background_) {
2559  invalidateAll_ = true;
2560  }
2561  if(invalidateAll_) {
2562  DBG_DP << "draw() with invalidateAll";
2563 
2564  // toggle invalidateAll_ first to allow regular invalidations
2565  invalidateAll_ = false;
2567 
2568  redraw_minimap();
2569  }
2570  }
2571 
2572  // invalidate animated terrain, units and haloes
2574 
2575  // Update and invalidate floating labels as necessary
2577 }
2578 
2580 {
2581  // This should render the game map and units.
2582  // It is not responsible for halos and floating labels.
2583  //DBG_DP << "display::render";
2584 
2585  // No need to render if we aren't going to draw anything.
2586  if(prevent_draw_) {
2587  DBG_DP << "render prevented";
2588  return;
2589  }
2590 
2591  // render to the offscreen buffer
2592  auto target_setter = draw::set_render_target(front_);
2593  draw();
2594 
2595  // update the minimap texture, if necessary
2596  // TODO: highdpi - high DPI minimap
2597  const rect& area = minimap_area();
2598  if(!area.empty() &&
2599  (!minimap_ || minimap_.w() > area.w || minimap_.h() > area.h))
2600  {
2602  if(!minimap_) {
2603  ERR_DP << "error creating minimap";
2604  return;
2605  }
2606  }
2607 }
2608 
2609 bool display::expose(const rect& region)
2610 {
2611  if(prevent_draw_) {
2612  DBG_DP << "draw prevented";
2613  return false;
2614  }
2615 
2616  rect clipped_region = draw::get_clip().intersect(region);
2617 
2618  // Blit from the pre-rendered front buffer.
2619  if(clipped_region.overlaps(map_outside_area())) {
2620  front_.set_src(clipped_region);
2621  draw::blit(front_, clipped_region);
2622  front_.clear_src();
2623  }
2624 
2625  // Render halos.
2626  halo_man_.render(clipped_region);
2627 
2628  // Render UI elements.
2629  // Ideally buttons would be drawn as part of panels,
2630  // but they are currently TLDs so they draw themselves.
2631  // This also means they draw over tooltips...
2632  draw_all_panels(clipped_region);
2633  draw_reports(clipped_region);
2634  if(clipped_region.overlaps(minimap_area())) {
2635  draw_minimap();
2636  }
2637 
2638  // Floating labels should probably be separated by type,
2639  // but they aren't so they all get drawn here.
2641 
2642  // If there's a fade, apply it over everything
2643  if(fade_color_.a) {
2644  draw::fill(map_outside_area().intersect(region), fade_color_);
2645  }
2646 
2647  DBG_DP << "display::expose " << region;
2648 
2649  // The display covers the entire screen.
2650  // We will always be drawing something.
2651  return true;
2652 }
2653 
2655 {
2656  assert(!map_screenshot_);
2657  // There's no good way to determine this, as themes can put things
2658  // anywhere. Just return the entire game canvas.
2659  return video::game_canvas();
2660 }
2661 
2663 {
2664  if(video::headless()) {
2665  return;
2666  }
2667 
2668  // We ignore any logical offset on the underlying window buffer.
2669  // Render buffer size is always a simple multiple of the draw area.
2670  rect darea = video::game_canvas();
2671  rect oarea = darea * video::get_pixel_scale();
2672 
2673  // Check that the front buffer size is correct.
2674  // Buffers are always resized together, so we only need to check one.
2676  point dsize = front_.draw_size();
2677  bool raw_size_changed = size.x != oarea.w || size.y != oarea.h;
2678  bool draw_size_changed = dsize.x != darea.w || dsize.y != darea.h;
2679  if (!raw_size_changed && !draw_size_changed) {
2680  // buffers are fine
2681  return;
2682  }
2683 
2684  if(raw_size_changed) {
2685  LOG_DP << "regenerating render buffers as " << oarea;
2686  front_ = texture(oarea.w, oarea.h, SDL_TEXTUREACCESS_TARGET);
2687  back_ = texture(oarea.w, oarea.h, SDL_TEXTUREACCESS_TARGET);
2688  }
2689  if(raw_size_changed || draw_size_changed) {
2690  LOG_DP << "updating render buffer draw size to " << darea;
2691  front_.set_draw_size(darea.w, darea.h);
2692  back_.set_draw_size(darea.w, darea.h);
2693  }
2694 
2695  // Fill entire texture with black, just in case
2696  for(int i = 0; i < 2; ++i) {
2697  auto setter = draw::set_render_target(i ? back_ : front_);
2698  draw::fill(0,0,0);
2699  }
2700 
2701  // Fill in the background area on both textures.
2703 
2704  queue_rerender();
2705 }
2706 
2708 {
2709  // This could be optimized to avoid the map area,
2710  // but it's only called on game creation or zoom anyway.
2711  const SDL_Rect clip_rect = map_outside_area();
2713  for(int i = 0; i < 2; ++i) {
2714  auto setter = draw::set_render_target(i ? back_ : front_);
2715  if(bgtex) {
2716  draw::tiled(bgtex, clip_rect);
2717  } else {
2718  draw::fill(clip_rect, 0, 0, 0);
2719  }
2720  }
2721 }
2722 
2724 {
2725  return *map_labels_;
2726 }
2727 
2729 {
2730  return *map_labels_;
2731 }
2732 
2734 {
2735  return map_area();
2736 }
2737 
2739 // log_scope("display::draw_invalidated");
2740  SDL_Rect clip_rect = get_clip_rect();
2741  auto clipper = draw::reduce_clip(clip_rect);
2742  DBG_DP << "drawing " << invalidated_.size() << " invalidated hexes"
2743  << " with clip " << clip_rect;
2744  for (const map_location& loc : invalidated_) {
2745  int xpos = get_location_x(loc);
2746  int ypos = get_location_y(loc);
2747 
2748  //const bool on_map = get_map().on_board(loc);
2749  rect hex_rect(xpos, ypos, zoom_, zoom_);
2750  if(!hex_rect.overlaps(clip_rect)) {
2751  continue;
2752  }
2753  draw_hex(loc);
2754  drawn_hexes_+=1;
2755  draw_manager::invalidate_region(hex_rect.intersect(clip_rect));
2756  }
2757  invalidated_hexes_ += invalidated_.size();
2758 
2759  // The unit drawer can't function without teams
2760  if(dc_->teams().empty()) {
2761  return;
2762  }
2763 
2764  unit_drawer drawer = unit_drawer(*this);
2765 
2766  for(const map_location& loc : invalidated_) {
2767  unit_map::const_iterator u_it = dc_->units().find(loc);
2769  if(u_it != dc_->units().end()
2770  && (request == exclusive_unit_draw_requests_.end() || request->second == u_it->id())) {
2771  drawer.redraw_unit(*u_it);
2772  }
2773  }
2774 }
2775 
2777 {
2778  int xpos = get_location_x(loc);
2779  int ypos = get_location_y(loc);
2780  const bool on_map = get_map().on_board(loc);
2781  const time_of_day& tod = get_time_of_day(loc);
2782  const int zoom = int(zoom_);
2783  SDL_Rect dest{xpos, ypos, zoom, zoom};
2784 
2785  int num_images_fg = 0;
2786  int num_images_bg = 0;
2787 
2788  if(!shrouded(loc)) {
2789  // unshrouded terrain (the normal case)
2790  get_terrain_images(loc, tod.id, BACKGROUND); // updates terrain_image_vector_
2792  num_images_bg = terrain_image_vector_.size();
2793 
2794  get_terrain_images(loc, tod.id, FOREGROUND); // updates terrain_image_vector_
2796  num_images_fg = terrain_image_vector_.size();
2797 
2798  // Draw the grid, if that's been enabled
2799  if(preferences::grid()) {
2805  image::get_texture(grid_bottom, image::TOD_COLORED));
2806  }
2807  }
2808 
2809  const t_translation::terrain_code terrain = get_map().get_terrain(loc);
2810  const terrain_type& terrain_info = get_map().get_terrain_info(terrain);
2811  const double submerge = terrain_info.unit_submerge();
2812 
2813  if(!shrouded(loc)) {
2814  auto it = get_overlays().find(loc);
2815  if(it != get_overlays().end()) {
2816  std::vector<overlay>& overlays = it->second;
2817  if(overlays.size() != 0) {
2818  tod_color tod_col = tod.color + color_adjust_;
2819  image::light_string lt = image::get_light_string(-1, tod_col.r, tod_col.g, tod_col.b);
2820 
2821  for(const overlay& ov : overlays) {
2822  bool item_visible_for_team = true;
2823  if(dont_show_all_ && !ov.team_name.empty()) {
2824  //dont_show_all_ imples that viewing_team()is a valid index to get_teams()
2825  const std::string& current_team_name = get_teams()[viewing_team()].team_name();
2826  const std::vector<std::string>& current_team_names = utils::split(current_team_name);
2827  const std::vector<std::string>& team_names = utils::split(ov.team_name);
2828 
2829  item_visible_for_team = std::find_first_of(team_names.begin(), team_names.end(),
2830  current_team_names.begin(), current_team_names.end()) != team_names.end();
2831  }
2832  if(item_visible_for_team && !(fogged(loc) && !ov.visible_in_fog))
2833  {
2834  point isize = image::get_size(ov.image, image::HEXED);
2835  std::string ipf = ov.image;
2836  if(ov.submerge) {
2837  // Adjust submerge appropriately
2838  double sub = submerge * ov.submerge;
2839  // Shift the image so the waterline remains static.
2840  // This is so that units swimming there look okay.
2841  int shift = isize.y * (sub - submerge);
2842  add_submerge_ipf_mod(ipf, isize.y, sub, shift);
2843  }
2844  const texture tex = ov.image.find("~NO_TOD_SHIFT()") == std::string::npos
2845  ? image::get_lighted_texture(ipf, lt)
2847  drawing_buffer_add(LAYER_TERRAIN_BG, loc, dest, tex);
2848  }
2849  }
2850  }
2851  }
2852  }
2853 
2854  if(!shrouded(loc)) {
2855  // village-control flags.
2856  drawing_buffer_add(LAYER_TERRAIN_BG, loc, dest, get_flag(loc));
2857  }
2858 
2859  // Draw the time-of-day mask on top of the terrain in the hex.
2860  // tod may differ from tod if hex is illuminated.
2861  const std::string& tod_hex_mask = tod.image_mask;
2862  if(tod_hex_mask1 || tod_hex_mask2) {
2863  auto& a = drawing_buffer_add(LAYER_TERRAIN_FG, loc, dest, tod_hex_mask1);
2864  a.alpha_mod = tod_hex_alpha1;
2865  auto& b = drawing_buffer_add(LAYER_TERRAIN_FG, loc, dest, tod_hex_mask2);
2866  b.alpha_mod = tod_hex_alpha2;
2867  } else if(!tod_hex_mask.empty()) {
2869  image::get_texture(tod_hex_mask,image::HEXED));
2870  }
2871 
2872  // Paint mouseover overlays
2873  if(loc == mouseoverHex_
2874  && (on_map || (in_editor() && get_map().on_board_with_border(loc)))
2875  && !map_screenshot_
2876  && bool(mouseover_hex_overlay_))
2877  {
2878  const uint8_t alpha = 196;
2880  loc, dest, mouseover_hex_overlay_);
2881  bh.alpha_mod = alpha;
2882  }
2883 
2884  // Paint arrows
2885  arrows_map_t::const_iterator arrows_in_hex = arrows_map_.find(loc);
2886  if(arrows_in_hex != arrows_map_.end()) {
2887  for (arrow* const a : arrows_in_hex->second) {
2888  a->draw_hex(loc);
2889  }
2890  }
2891 
2892  // Apply shroud, fog and linger overlay
2893 
2894  if(shrouded(loc)) {
2895  // We apply void also on off-map tiles
2896  // to shroud the half-hexes too
2897  const std::string& shroud_image = get_variant(shroud_images_, loc);
2899  image::get_texture(shroud_image, image::TOD_COLORED));
2900  } else if(fogged(loc)) {
2901  const std::string& fog_image = get_variant(fog_images_, loc);
2904  }
2905 
2906  if(!shrouded(loc)) {
2908  }
2909 
2910  if (on_map) {
2911  texture bg = image::get_texture("misc/single-pixel.png");
2912  color_t bg_col = {0, 0, 0, 0xaa};
2913  if (draw_coordinates_) {
2914  int off_x = xpos + hex_size()/2;
2915  int off_y = ypos + hex_size()/2;
2916  texture text = font::pango_render_text(lexical_cast<std::string>(loc), font::SIZE_SMALL, font::NORMAL_COLOR);
2917  off_x -= text.w() / 2;
2918  off_y -= text.h() / 2;
2919  if (draw_terrain_codes_) {
2920  off_y -= text.h() / 2;
2921  }
2922  if (draw_num_of_bitmaps_) {
2923  off_y -= text.h() / 2;
2924  }
2925  rect tdest {off_x, off_y, text.w(), text.h()};
2926  drawing_buffer_add(LAYER_FOG_SHROUD, loc, tdest, bg)
2927  .set_color_and_alpha(bg_col);
2928  drawing_buffer_add(LAYER_FOG_SHROUD, loc, tdest, text);
2929  }
2930  if (draw_terrain_codes_ && (game_config::debug || !shrouded(loc))) {
2931  int off_x = xpos + hex_size()/2;
2932  int off_y = ypos + hex_size()/2;
2933  texture text = font::pango_render_text(lexical_cast<std::string>(get_map().get_terrain(loc)), font::SIZE_SMALL, font::NORMAL_COLOR);
2934  off_x -= text.w() / 2;
2935  off_y -= text.h() / 2;
2937  off_y += text.h() / 2;
2938  } else if (draw_num_of_bitmaps_ && !draw_coordinates_) {
2939  off_y -= text.h() / 2;
2940  }
2941  rect tdest {off_x, off_y, text.w(), text.h()};
2942  drawing_buffer_add(LAYER_FOG_SHROUD, loc, tdest, bg)
2943  .set_color_and_alpha(bg_col);
2944  drawing_buffer_add(LAYER_FOG_SHROUD, loc, tdest, text);
2945  }
2946  if (draw_num_of_bitmaps_) {
2947  int off_x = xpos + hex_size()/2;
2948  int off_y = ypos + hex_size()/2;
2949  texture text = font::pango_render_text(std::to_string(num_images_bg + num_images_fg), font::SIZE_SMALL, font::NORMAL_COLOR);
2950  off_x -= text.w() / 2;
2951  off_y -= text.h() / 2;
2952  if (draw_coordinates_) {
2953  off_y += text.h() / 2;
2954  }
2955  if (draw_terrain_codes_) {
2956  off_y += text.h() / 2;
2957  }
2958  rect tdest {off_x, off_y, text.w(), text.h()};
2959  drawing_buffer_add(LAYER_FOG_SHROUD, loc, tdest, bg)
2960  .set_color_and_alpha(bg_col);
2961  drawing_buffer_add(LAYER_FOG_SHROUD, loc, tdest, text);
2962  }
2963  }
2964 
2965  if(debug_foreground) {
2967  image::get_texture("terrain/foreground.png", image::TOD_COLORED));
2968  }
2969 
2970 }
2971 
2972 /**
2973  * Redraws the specified report (if anything has changed).
2974  * If a config is not supplied, it will be generated via
2975  * reports::generate_report().
2976  */
2977 void display::refresh_report(const std::string& report_name, const config * new_cfg)
2978 {
2979  const theme::status_item *item = theme_.get_status_item(report_name);
2980  if (!item) {
2981  // This should be a warning, but unfortunately there are too many
2982  // unused reports to easily deal with.
2983  //WRN_DP << "no report '" << report_name << "' in theme";
2984  return;
2985  }
2986 
2987  // Now we will need the config. Generate one if needed.
2988 
2990 
2991  if (resources::controller) {
2993  }
2994 
2995  reports::context temp_context = reports::context(*dc_, *this, *resources::tod_manager, wb_.lock(), mhb);
2996 
2997  const config generated_cfg = new_cfg ? config() : reports_object_->generate_report(report_name, temp_context);
2998  if ( new_cfg == nullptr )
2999  new_cfg = &generated_cfg;
3000 
3001  rect& loc = reportLocations_[report_name];
3002  const rect& new_loc = item->location(video::game_canvas());
3003  config &report = reports_[report_name];
3004 
3005  // Report and its location is unchanged since last time. Do nothing.
3006  if (loc == new_loc && report == *new_cfg) {
3007  return;
3008  }
3009 
3010  DBG_DP << "updating report: " << report_name;
3011 
3012  // Mark both old and new locations for redraw.
3015 
3016  // Update the config and current location.
3017  report = *new_cfg;
3018  loc = new_loc;
3019 
3020  // Not 100% sure this is okay
3021  // but it seems to be working so i'm not changing it.
3023 
3024  if (report.empty()) return;
3025 
3026  // Add prefix, postfix elements.
3027  // Make sure that they get the same tooltip
3028  // as the guys around them.
3029  std::string str = item->prefix();
3030  if (!str.empty()) {
3031  config &e = report.add_child_at("element", config(), 0);
3032  e["text"] = str;
3033  e["tooltip"] = report.child("element")["tooltip"];
3034  }
3035  str = item->postfix();
3036  if (!str.empty()) {
3037  config &e = report.add_child("element");
3038  e["text"] = str;
3039  e["tooltip"] = report.child("element", -1)["tooltip"];
3040  }
3041 
3042  // Do a fake run of drawing the report, so tooltips can be determined.
3043  // TODO: this is horrible, refactor reports to actually make sense
3044  draw_report(report_name, true);
3045 }
3046 
3047 void display::draw_report(const std::string& report_name, bool tooltip_test)
3048 {
3049  const theme::status_item *item = theme_.get_status_item(report_name);
3050  if (!item) {
3051  // This should be a warning, but unfortunately there are too many
3052  // unused reports to easily deal with.
3053  //WRN_DP << "no report '" << report_name << "' in theme";
3054  return;
3055  }
3056 
3057  const rect& loc = reportLocations_[report_name];
3058  const config& report = reports_[report_name];
3059 
3060  int x = loc.x, y = loc.y;
3061 
3062  // Loop through and display each report element.
3063  int tallest = 0;
3064  int image_count = 0;
3065  bool used_ellipsis = false;
3066  std::ostringstream ellipsis_tooltip;
3067  SDL_Rect ellipsis_area = loc;
3068 
3069  for (config::const_child_itors elements = report.child_range("element");
3070  elements.begin() != elements.end(); elements.pop_front())
3071  {
3072  SDL_Rect area {x, y, loc.w + loc.x - x, loc.h + loc.y - y};
3073  if (area.h <= 0) break;
3074 
3075  std::string t = elements.front()["text"];
3076  if (!t.empty())
3077  {
3078  if (used_ellipsis) goto skip_element;
3079 
3080  // Draw a text element.
3082  bool eol = false;
3083  if (t[t.size() - 1] == '\n') {
3084  eol = true;
3085  t = t.substr(0, t.size() - 1);
3086  }
3087  // If stripping left the text empty, skip it.
3088  if (t.empty()) {
3089  // Blank text has a null size when rendered.
3090  // It does not, however, have a null size when the size
3091  // is requested with get_size(). Hence this check.
3092  continue;
3093  }
3094  text.set_link_aware(false)
3095  .set_text(t, true);
3097  .set_font_size(item->font_size())
3098  .set_font_style(font::pango_text::STYLE_NORMAL)
3099  .set_alignment(PANGO_ALIGN_LEFT)
3101  .set_maximum_width(area.w)
3102  .set_maximum_height(area.h, false)
3103  .set_ellipse_mode(PANGO_ELLIPSIZE_END)
3105 
3106  point tsize = text.get_size();
3107 
3108  // check if next element is text with almost no space to show it
3109  const int minimal_text = 12; // width in pixels
3110  config::const_child_iterator ee = elements.begin();
3111  if (!eol && loc.w - (x - loc.x + tsize.x) < minimal_text &&
3112  ++ee != elements.end() && !(*ee)["text"].empty())
3113  {
3114  // make this element longer to trigger rendering of ellipsis
3115  // (to indicate that next elements have not enough space)
3116  //NOTE this space should be longer than minimal_text pixels
3117  t = t + " ";
3118  text.set_text(t, true);
3119  tsize = text.get_size();
3120  // use the area of this element for next tooltips
3121  used_ellipsis = true;
3122  ellipsis_area.x = x;
3123  ellipsis_area.y = y;
3124  ellipsis_area.w = tsize.x;
3125  ellipsis_area.h = tsize.y;
3126  }
3127 
3128  area.w = tsize.x;
3129  area.h = tsize.y;
3130  if (!tooltip_test) {
3131  draw::blit(text.render_and_get_texture(), area);
3132  }
3133  if (area.h > tallest) {
3134  tallest = area.h;
3135  }
3136  if (eol) {
3137  x = loc.x;
3138  y += tallest;
3139  tallest = 0;
3140  } else {
3141  x += area.w;
3142  }
3143  }
3144  else if (!(t = elements.front()["image"].str()).empty())
3145  {
3146  if (used_ellipsis) goto skip_element;
3147 
3148  // Draw an image element.
3149  texture img(image::get_texture(t));
3150 
3151  if (!img) {
3152  ERR_DP << "could not find image for report: '" << t << "'";
3153  continue;
3154  }
3155 
3156  if (area.w < img.w() && image_count) {
3157  // We have more than one image, and this one doesn't fit.
3159  used_ellipsis = true;
3160  }
3161 
3162  if (img.w() < area.w) area.w = img.w();
3163  if (img.h() < area.h) area.h = img.h();
3164  if (!tooltip_test) {
3165  draw::blit(img, area);
3166  }
3167 
3168  ++image_count;
3169  if (area.h > tallest) {
3170  tallest = area.h;
3171  }
3172 
3173  if (!used_ellipsis) {
3174  x += area.w;
3175  } else {
3176  ellipsis_area = area;
3177  }
3178  }
3179  else
3180  {
3181  // No text nor image, skip this element
3182  continue;
3183  }
3184 
3185  skip_element:
3186  t = elements.front()["tooltip"].t_str().c_str();
3187  if (!t.empty()) {
3188  if (tooltip_test && !used_ellipsis) {
3189  tooltips::add_tooltip(area, t, elements.front()["help"].t_str().c_str());
3190  } else {
3191  // Collect all tooltips for the ellipsis.
3192  // TODO: need a better separator
3193  // TODO: assign an action
3194  ellipsis_tooltip << t;
3195  config::const_child_iterator ee = elements.begin();
3196  if (++ee != elements.end())
3197  ellipsis_tooltip << "\n _________\n\n";
3198  }
3199  }
3200  }
3201 
3202  if (tooltip_test && used_ellipsis) {
3203  tooltips::add_tooltip(ellipsis_area, ellipsis_tooltip.str());
3204  }
3205 }
3206 
3207 bool display::draw_reports(const rect& region)
3208 {
3209  bool drew = false;
3210  for(const auto& it : reports_) {
3211  const std::string& name = it.first;
3212  const rect& loc = reportLocations_[name];
3213  if(loc.overlaps(region)) {
3214  draw_report(name);
3215  drew = true;
3216  }
3217  }
3218  return drew;
3219 }
3220 
3222 {
3223  DBG_DP << "invalidate_all()";
3224  invalidateAll_ = true;
3225  invalidated_.clear();
3226 }
3227 
3229 {
3230  if(invalidateAll_)
3231  return false;
3232 
3233  bool tmp;
3234  tmp = invalidated_.insert(loc).second;
3235  return tmp;
3236 }
3237 
3238 bool display::invalidate(const std::set<map_location>& locs)
3239 {
3240  if(invalidateAll_)
3241  return false;
3242  bool ret = false;
3243  for (const map_location& loc : locs) {
3244  ret = invalidated_.insert(loc).second || ret;
3245  }
3246  return ret;
3247 }
3248 
3249 bool display::propagate_invalidation(const std::set<map_location>& locs)
3250 {
3251  if(invalidateAll_)
3252  return false;
3253 
3254  if(locs.size()<=1)
3255  return false; // propagation never needed
3256 
3257  bool result = false;
3258  {
3259  // search the first hex invalidated (if any)
3260  std::set<map_location>::const_iterator i = locs.begin();
3261  for(; i != locs.end() && invalidated_.count(*i) == 0 ; ++i) {}
3262 
3263  if (i != locs.end()) {
3264 
3265  // propagate invalidation
3266  // 'i' is already in, but I suspect that splitting the range is bad
3267  // especially because locs are often adjacents
3268  size_t previous_size = invalidated_.size();
3269  invalidated_.insert(locs.begin(), locs.end());
3270  result = previous_size < invalidated_.size();
3271  }
3272  }
3273  return result;
3274 }
3275 
3277 {
3278  return invalidate_locations_in_rect(map_area().intersect(rect));
3279 }
3280 
3282 {
3283  if(invalidateAll_)
3284  return false;
3285 
3286  DBG_DP << "invalidating locations in " << rect;
3287 
3288  bool result = false;
3289  for(const map_location& loc : hexes_under_rect(rect)) {
3290  //DBG_DP << "invalidating " << loc.x << ',' << loc.y;
3291  result |= invalidate(loc);
3292  }
3293  return result;
3294 }
3295 
3297 {
3298  if(get_map().is_village(loc)) {
3299  const int owner = dc_->village_owner(loc) - 1;
3300  if(owner >= 0 && flags_[owner].need_update()
3301  && (!fogged(loc) || !dc_->teams()[currentTeam_].is_enemy(owner + 1))) {
3302  invalidate(loc);
3303  }
3304  }
3305 }
3306 
3308 {
3309  // There are timing issues with this, but i'm not touching it.
3312  if(animate_map_) {
3313  for(const map_location& loc : get_visible_hexes()) {
3314  if(shrouded(loc))
3315  continue;
3316  if(builder_->update_animation(loc)) {
3317  invalidate(loc);
3318  } else {
3320  }
3321  }
3322  }
3323 
3324  for(const unit& u : dc_->units()) {
3325  u.anim_comp().refresh();
3326  }
3327  for(const unit* u : *fake_unit_man_) {
3328  u->anim_comp().refresh();
3329  }
3330 
3331  bool new_inval;
3332  do {
3333  new_inval = false;
3334  for(const unit& u : dc_->units()) {
3335  new_inval |= u.anim_comp().invalidate(*this);
3336  }
3337  for(const unit* u : *fake_unit_man_) {
3338  new_inval |= u->anim_comp().invalidate(*this);
3339  }
3340  } while(new_inval);
3341 
3342  halo_man_.update();
3343 }
3344 
3346 {
3347  for(const unit & u : dc_->units()) {
3348  u.anim_comp().set_standing();
3349  }
3350 }
3351 
3353 {
3354  for(const map_location& loc : arrow.get_path()) {
3355  arrows_map_[loc].push_back(&arrow);
3356  }
3357 }
3358 
3360 {
3361  for(const map_location& loc : arrow.get_path()) {
3362  arrows_map_[loc].remove(&arrow);
3363  }
3364 }
3365 
3367 {
3368  for(const map_location& loc : arrow.get_previous_path()) {
3369  arrows_map_[loc].remove(&arrow);
3370  }
3371 
3372  for(const map_location& loc : arrow.get_path()) {
3373  arrows_map_[loc].push_back(&arrow);
3374  }
3375 }
3376 
3378 {
3379  const SDL_Rect& rect = map_area();
3380  return pixel_position_to_hex(xpos_ + rect.x + rect.w / 2 , ypos_ + rect.y + rect.h / 2 );
3381 }
3382 
3383 void display::write(config& cfg) const
3384 {
3385  cfg["view_locked"] = view_locked_;
3386  cfg["color_adjust_red"] = color_adjust_.r;
3387  cfg["color_adjust_green"] = color_adjust_.g;
3388  cfg["color_adjust_blue"] = color_adjust_.b;
3389  get_middle_location().write(cfg.add_child("location"));
3390 }
3391 
3392 void display::read(const config& cfg)
3393 {
3394  view_locked_ = cfg["view_locked"].to_bool(false);
3395  color_adjust_.r = cfg["color_adjust_red"].to_int(0);
3396  color_adjust_.g = cfg["color_adjust_green"].to_int(0);
3397  color_adjust_.b = cfg["color_adjust_blue"].to_int(0);
3398 }
3399 
3401 {
3402  if (!reach_map_changed_) return;
3403  if (reach_map_.empty() != reach_map_old_.empty()) {
3404  // Invalidate everything except the non-darkened tiles
3405  reach_map &full = reach_map_.empty() ? reach_map_old_ : reach_map_;
3406 
3407  for (const auto& hex : get_visible_hexes()) {
3408  reach_map::iterator reach = full.find(hex);
3409  if (reach == full.end()) {
3410  // Location needs to be darkened or brightened
3411  invalidate(hex);
3412  } else if (reach->second != 1) {
3413  // Number needs to be displayed or cleared
3414  invalidate(hex);
3415  }
3416  }
3417  } else if (!reach_map_.empty()) {
3418  // Invalidate only changes
3419  reach_map::iterator reach, reach_old;
3420  for (reach = reach_map_.begin(); reach != reach_map_.end(); ++reach) {
3421  reach_old = reach_map_old_.find(reach->first);
3422  if (reach_old == reach_map_old_.end()) {
3423  invalidate(reach->first);
3424  } else {
3425  if (reach_old->second != reach->second) {
3426  invalidate(reach->first);
3427  }
3428  reach_map_old_.erase(reach_old);
3429  }
3430  }
3431  for (reach_old = reach_map_old_.begin(); reach_old != reach_map_old_.end(); ++reach_old) {
3432  invalidate(reach_old->first);
3433  }
3434  }
3436  reach_map_changed_ = false;
3437 }
3438 
3439 display *display::singleton_ = nullptr;
Drawing functions, for drawing things on the screen.
std::shared_ptr< gui::button > find_action_button(const std::string &id)
Retrieves a pointer to a theme UI button.
Definition: display.cpp:826
std::size_t font_size() const
Definition: theme.hpp:114
play_controller * controller
Definition: resources.cpp:22
TYPE
Used to specify the rendering format of images.
Definition: picture.hpp:236
void drawing_buffer_commit()
Draws the drawing_buffer_ and clears it.
Definition: display.cpp:1279
int village_owner(const map_location &loc) const
Given the location of a village, will return the 1-based number of the team that currently owns it...
virtual bool expose(const rect &region) override
Paint the indicated region to the screen.
Definition: display.cpp:2609
void draw_minimap_units()
Definition: display.cpp:1822
const std::string & get_modifications() const
Definition: picture.hpp:86
int zoom_index_
Definition: display.hpp:752
void recalculate_shroud()
Definition: label.cpp:278
bool discard_previous
An announcement according these options should replace the previous announce (typical of fast announc...
Definition: display.hpp:631
void draw_floating_labels()
const std::string & icon() const
Definition: theme.hpp:110
virtual void select_hex(map_location hex)
Definition: display.cpp:1631
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:402
::tod_manager * tod_manager
Definition: resources.cpp:30
Arrows destined to be drawn on the map.
reports * reports_object_
Definition: display.hpp:764
unit_iterator end()
Definition: map.hpp:429
bool minimap_draw_units()
Definition: general.cpp:848
void write(config &cfg) const
Definition: display.cpp:3383
hotkey::command_executor * get_hotkey_command_executor() override
Optionally get a command executor to handle context menu events.
const rect & minimap_area() const
mapx is the width of the portion of the display which shows the game area.
Definition: display.cpp:515
void queue_rerender()
Marks everything for rendering including all tiles and sidebar.
Definition: display.cpp:2426
bool show_fps()
Definition: general.cpp:888
bool vflip
Whether to mirror vertically on draw.
Definition: display.hpp:972
const team & get_team(int side) const
void remove_floating_label(int handle, int fadeout)
removes the floating label given by &#39;handle&#39; from the screen
surface read_pixels(SDL_Rect *r)
Copy back a portion of the render target that is already drawn.
Definition: video.cpp:527
void invalidate_game_status()
Function to invalidate the game status displayed on the sidebar.
Definition: display.hpp:295
Small struct to store and manipulate ToD color adjusts.
Definition: time_of_day.hpp:27
constexpr bool is_odd(T num)
Definition: math.hpp:36
int get_pixel_scale()
Get the current active pixel scale multiplier.
Definition: video.cpp:435
void adjust_color_overlay(int r, int g, int b)
Add r,g,b to the colors for all images displayed on the map.
Definition: display.cpp:451
const arrow_path_t & get_path() const
Definition: arrow.cpp:123
#define DefaultZoom
Definition: display.cpp:84
std::string image_mask
The image that is to be laid over all images while this time of day lasts.
Definition: time_of_day.hpp:96
void get_adjacent_tiles(const map_location &a, map_location *res)
Function which, given a location, will place all adjacent locations in res.
Definition: location.cpp:475
uint8_t highlight
Strength of highlight effect to apply, if any.
Definition: display.hpp:980
void invalidate_animations()
Function to invalidate animated terrains and units which may have changed.
Definition: display.cpp:3307
#define ERR_DP
Definition: display.cpp:76
bool invalidate(const map_location &loc)
Function to invalidate a specific tile for redrawing.
Definition: display.cpp:3228
color_t font_rgb() const
Definition: theme.hpp:115
static int hex_size()
Function which returns the size of a hex in pixels (from top tip to bottom tip or left edge to right ...
Definition: display.hpp:252
This class represents a single unit of a specific type.
Definition: unit.hpp:120
int add_tooltip(const SDL_Rect &origin, const std::string &message, const std::string &action)
Definition: tooltips.cpp:237
texture tod_hex_mask2
Definition: display.hpp:785
int w() const
The draw-space width of the texture, in pixels.
Definition: texture.hpp:105
bool animate_water_
Local version of preferences::animate_water, used to detect when it&#39;s changed.
Definition: display.hpp:799
virtual void notify_observers()
tod_color color
The color modifications that should be made to the game board to reflect the time of day...
static void toggle_benchmark()
Toggle to continuously redraw the screen.
Definition: display.cpp:1330
CKey keys_
Definition: display.hpp:793
bool set_text(const std::string &text, const bool markedup)
Sets the text to render.
Definition: text.cpp:330
void set_clip_rect(const SDL_Rect &r)
bool set_zoom(bool increase)
Zooms the display in (true) or out (false).
Definition: display.cpp:1979
void change_display_context(const display_context *dc)
Definition: display.cpp:496
const map_location hex_clicked_on(int x, int y) const
given x,y co-ordinates of an onscreen pixel, will return the location of the hex that this pixel corr...
Definition: display.cpp:590
boost::circular_buffer< unsigned > frametimes_
Definition: display.hpp:769
int ypos_
Definition: display.hpp:744
void pump_and_draw()
pump() then immediately draw()
Definition: events.hpp:151
bool reach_map_changed_
Definition: display.hpp:1106
static double get_zoom_factor()
Returns the current zoom factor.
Definition: display.hpp:255
drawing_buffer drawing_buffer_
Definition: display.hpp:1036
texture render_and_get_texture()
Returns the cached texture, or creates a new one otherwise.
Definition: text.cpp:101
void set_playing_team(std::size_t team)
set_playing_team sets the team whose turn it currently is
Definition: display.cpp:403
const map_location pixel_position_to_hex(int x, int y) const
given x,y co-ordinates of a pixel on the map, will return the location of the hex that this pixel cor...
Definition: display.cpp:604
void scroll_floating_labels(double xmove, double ymove)
moves all floating labels that have &#39;scroll_mode&#39; set to ANCHOR_LABEL_MAP
#define a
void clear_src()
Clear the source region.
Definition: texture.hpp:167
int draw_delay()
Definition: general.cpp:898
double unit_submerge() const
Definition: terrain.hpp:138
void set_src(const rect &r)
Set the source region of the texture used for drawing operations.
Definition: texture.cpp:129
bool minimap_movement_coding()
Definition: general.cpp:828
int h() const
The draw-space height of the texture, in pixels.
Definition: texture.hpp:114
point get_raw_size() const
The raw internal texture size.
Definition: texture.cpp:112
bool in_dialog()
Definition: show_dialog.cpp:60
constexpr uint8_t float_to_color(double n)
Convert a double in the range [0.0,1.0] to an 8-bit colour value.
Definition: color.hpp:268
virtual void draw_invalidated()
Only called when there&#39;s actual redrawing to do.
Definition: display.cpp:2738
map_location mouseoverHex_
Definition: display.hpp:792
void init_flags()
Init the flag list and the team colors used by ~TC.
Definition: display.cpp:292
Manages a list of fake units for the display object.
const rect & mini_map_location(const SDL_Rect &screen) const
Definition: theme.hpp:275
uint8_t r_mod
Colour modifiers.
Definition: display.hpp:976
void update_floating_labels()
Mouseover overlay used by editor.
Definition: display.hpp:825
virtual void draw_hex(const map_location &hex)
Definition: arrow.cpp:139
child_itors child_range(config_key_type key)
Definition: config.cpp:344
std::string id
Definition: time_of_day.hpp:90
const int SIZE_BUTTON_SMALL
Definition: constants.cpp:26
bool draw_coordinates_
Debug flag - overlay x,y coords on tiles.
Definition: display.hpp:1123
int current_refresh_rate()
The refresh rate of the screen.
Definition: video.cpp:440
int scroll_speed()
Definition: general.cpp:784
void set_lifetime(int lifetime, int fadeout=100)
static lg::log_domain log_display("display")
display(const display_context *dc, std::weak_ptr< wb::manager > wb, reports &reports_object, const std::string &theme_id, const config &level)
Definition: display.cpp:172
int fps_handle_
Handle for the label which displays frames per second.
Definition: display.hpp:1115
pango_text & set_link_aware(bool b)
Definition: text.cpp:502
bool show_everything() const
Definition: display.hpp:99
const rect & palette_location(const SDL_Rect &screen) const
Definition: theme.hpp:279
The class terrain_builder is constructed from a config object, and a gamemap object.
Definition: builder.hpp:48
map_location minimap_location_on(int x, int y)
given x,y co-ordinates of the mouse, will return the location of the hex in the minimap that the mous...
Definition: display.cpp:750
static SDL_Rect scaled_to_zoom(const SDL_Rect &r)
Scale the width and height of a rect by the current zoom factor.
Definition: display.hpp:261
const std::vector< std::string > items
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
const terrain_type & get_terrain_info(const t_translation::terrain_code &terrain) const
Definition: map.cpp:98
int viewing_side() const
Definition: display.hpp:112
void clear_fps_label()
Definition: display.cpp:1399
void set_tile_size(const unsigned int size)
Definition: general.cpp:663
void update()
Process animations, remove deleted halos, and invalidate screen regions now requiring redraw...
Definition: halo.cpp:446
reach_map reach_map_
Definition: display.hpp:1104
#define h
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:110
iterator & operator++()
increment y first, then when reaching bottom, increment x
Definition: display.cpp:656
std::vector< std::function< void(display &)> > redraw_observers_
Definition: display.hpp:1120
#define MaxZoom
Definition: display.cpp:87
double turbo_speed()
Definition: general.cpp:482
map_location selectedHex_
Definition: display.hpp:791
void set_font_size(int font_size)
static display * singleton_
Definition: display.hpp:1139
#define d
Belongs to a friendly side.
void draw()
Perform rendering of invalidated items.
Definition: display.cpp:2495
bool animate_water()
Definition: general.cpp:823
const int SIZE_PLUS
Definition: constants.cpp:29
Top half part of grid image.
Definition: display.hpp:824
void draw_panel(const theme::panel &panel)
Definition: display.cpp:1409
rect map_area() const
Returns the area used for the map.
Definition: display.cpp:547
Rectangular area of hexes, allowing to decide how the top and bottom edges handles the vertical shift...
Definition: display.hpp:305
An object to leave the synced context during draw or unsynced wml items when we don’t know whether w...
surface getMinimap(int w, int h, const gamemap &map, const team *vw, const std::map< map_location, unsigned int > *reach_map, bool ignore_terrain_disabled)
function to create the minimap for a given map the surface returned must be freed by the user ...
Definition: minimap.cpp:43
const std::vector< label > & labels() const
Definition: theme.hpp:255
point get_size()
Returns the size of the text, in drawing coordinates.
Definition: text.cpp:165
static unsigned int zoom_
The current zoom, in pixels (on screen) per 72 pixels (in the graphic assets), i.e., 72 means 100%.
Definition: display.hpp:751
pango_text & get_text_renderer()
Returns a reference to a static pango_text object.
Definition: text.cpp:965
void clear_redraw_observers()
Clear the redraw observers.
Definition: display.cpp:2490
Standard hexagonal tile mask applied, removing portions that don&#39;t fit.
Definition: picture.hpp:241
point get_size(const locator &i_locator, bool skip_cache)
Returns the width and height of an image.
Definition: picture.cpp:976
pango_text & set_ellipse_mode(const PangoEllipsizeMode ellipse_mode)
Definition: text.cpp:459
texture minimap_
Definition: display.hpp:757
drawing_layer
The layers to render something on.
Definition: display.hpp:819
Same as HEXED, but with Time of Day color tint applied.
Definition: picture.hpp:243
std::vector< texture > terrain_image_vector_
Definition: display.hpp:810
void redraw_minimap()
Schedule the minimap to be redrawn.
Definition: display.cpp:1757
virtual const gamemap & map() const =0
bool contains(int x, int y) const
Whether the given point lies within the rectangle.
Definition: rect.cpp:54
void reset_standing_animations()
Definition: display.cpp:3345
void remove_arrow(arrow &)
Definition: display.cpp:3359
void add_frame(int duration, const T &value, bool force_change=false)
Adds a frame to an animation.
filesystem::scoped_ostream ostream_file(const std::string &fname, std::ios_base::openmode mode, bool create_directory)
#define DBG_DP
Definition: display.cpp:79
Wrapper class to encapsulate creation and management of an SDL_Texture.
Definition: texture.hpp:32
void rect(const SDL_Rect &rect)
Draw a rectangle.
Definition: draw.cpp:141
uint8_t tod_hex_alpha2
Definition: display.hpp:787
void init_flags_for_side_internal(std::size_t side, const std::string &side_color)
Definition: display.cpp:313
texture mouseover_hex_overlay_
Definition: display.hpp:781
Unit and team statistics.
void invalidate_all()
Mark the entire screen as requiring redraw.
void queue_repaint()
Queues repainting to the screen, but doesn&#39;t rerender.
Definition: display.cpp:2478
very simple iterator to walk into the rect_of_hexes
Definition: display.hpp:312
iterator begin() const
Definition: display.cpp:669
const rect_of_hexes get_visible_hexes() const
Returns the rectangular area of visible hexes.
Definition: display.hpp:342
pango_text & set_alignment(const PangoAlignment alignment)
Definition: text.cpp:479
std::vector< std::shared_ptr< gui::button > > action_buttons_
Definition: display.hpp:779
map_location loc_
void clear_tooltips()
Definition: tooltips.cpp:179
std::map< std::string, rect > reportLocations_
Definition: display.hpp:776
virtual bool in_editor() const
Definition: display.hpp:203
bool team_valid() const
Definition: display.cpp:725
bool prevent_draw_
Definition: display.hpp:568
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition: general.hpp:84
#define b
const theme::action * action_pressed()
Definition: display.cpp:1689
clip_setter override_clip(const SDL_Rect &clip)
Override the clipping area.
Definition: draw.cpp:443
static const config & get_theme_config(const std::string &id)
Returns the saved config for the theme with the given ID.
Definition: theme.cpp:1004
const std::string & text() const
Definition: theme.hpp:108
rect game_canvas()
The game canvas area, in drawing coordinates.
Definition: video.cpp:381
std::vector< std::string > shroud_images_
Definition: display.hpp:789
Object which defines a time of day with associated bonuses, image, sounds etc.
Definition: time_of_day.hpp:56
bool exists(const image::locator &i_locator)
Returns true if the given image actually exists, without loading it.
Definition: picture.cpp:1028
void blindfold(bool flag)
Definition: display.cpp:502
bool draw_reports(const rect &region)
Draw all reports in the given region.
Definition: display.cpp:3207
void set_fade(const color_t &color)
Definition: display.cpp:2421
void parse_team_overlays()
Check the overlay_map for proper team-specific overlays to be displayed/hidden.
Definition: display.cpp:123
const config & options()
Definition: game.cpp:556
halo::manager halo_man_
Definition: display.hpp:694
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:75
point draw_size() const
The size of the texture in draw-space.
Definition: texture.hpp:122
Belongs to a non-friendly side; normally visualised by not displaying an orb.
rect max_map_area() const
Returns the maximum area used for the map regardless to resolution and view size. ...
Definition: display.cpp:530
blit_helper & set_color_and_alpha(color_t c)
Definition: display.hpp:983
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:87
render_target_setter set_render_target(const texture &t)
Set the given texture as the active render target.
Definition: draw.cpp:603
std::vector< texture > get_fog_shroud_images(const map_location &loc, image::TYPE image_type)
Definition: display.cpp:974
const std::string & id() const
Gets this unit&#39;s id.
Definition: unit.hpp:370
uint32_t last_frame_finished_
Definition: display.hpp:773
void get_terrain_images(const map_location &loc, const std::string &timeid, TERRAIN_TYPE terrain_type)
Definition: display.cpp:1060
const arrow_path_t & get_previous_path() const
Definition: arrow.cpp:128
double turbo_speed() const
Definition: display.cpp:2351
static bool zoom_at_max()
Definition: display.cpp:1969
void draw_minimap()
Actually draw the minimap.
Definition: display.cpp:1762
light_string get_light_string(int op, int r, int g, int b)
Returns the light_string for one light operation.
Definition: picture.cpp:649
const std::string & prefix() const
Definition: theme.hpp:132
std::map< std::string, texture > reportSurfaces_
Definition: display.hpp:777
void new_animation_frame()
Definition: animated.cpp:31
static unsigned calculate_fps(unsigned frametime)
Definition: display.cpp:1341
std::string label
What to show in the filter&#39;s drop-down list.
Definition: manager.cpp:217
static bool outside_area(const SDL_Rect &area, const int x, const int y)
Check if the bbox of the hex at x,y has pixels outside the area rectangle.
Definition: display.cpp:582
bool tile_fully_on_screen(const map_location &loc) const
Check if a tile is fully visible on screen.
Definition: display.cpp:2055
const theme::menu * menu_pressed()
Definition: display.cpp:1705
int w() const
Effective map width.
Definition: map.hpp:50
void draw_buttons()
Definition: display.cpp:952
Arrows destined to be drawn on the map.
Definition: arrow.hpp:30
surface screenshot(bool map_screenshot=false)
Capture a (map-)screenshot into a surface.
Definition: display.cpp:782
EXIT_STATUS start(const std::string &filename, bool take_screenshot, const std::string &screenshot_filename)
Main interface for launching the editor from the title screen.
Definition: editor_main.cpp:30
texture get_flag(const map_location &loc)
Definition: display.cpp:366
std::string get_orb_color(orb_status os)
Wrapper for the various preferences::unmoved_color(), moved_color(), etc methods, using the enum inst...
Definition: orb_status.cpp:40
bool font_rgb_set() const
Definition: theme.hpp:116
terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition: map.cpp:302
color_t smooth_blend(const color_t &c, uint8_t p) const
Blend smoothly with another color_t.
Definition: color.hpp:228
std::string get_user_data_dir()
Definition: filesystem.cpp:793
bool invalidate_visible_locations_in_rect(const SDL_Rect &rect)
Definition: display.cpp:3276
void recalculate_minimap()
Schedule the minimap for recalculation.
Definition: display.cpp:1737
void scroll_to_xy(int screenxpos, int screenypos, SCROLL_TYPE scroll_type, bool force=true)
Definition: display.cpp:2072
config & add_child_at(config_key_type key, const config &val, unsigned index)
Definition: config.cpp:546
bool valid() const
Definition: location.hpp:89
const rect & unit_image_location(const SDL_Rect &screen) const
Definition: theme.hpp:277
bool map_screenshot_
Used to indicate to drawing functions that we are doing a map screenshot.
Definition: display.hpp:1089
arrows_map_t arrows_map_
Maps the list of arrows for each location.
Definition: display.hpp:1132
const std::unique_ptr< map_labels > map_labels_
Definition: display.hpp:763
void clip(const SDL_Rect &r)
Clip this rectangle by the given rectangle.
Definition: rect.cpp:101
int xpos_
Position of the top-left corner of the viewport, in pixels.
Definition: display.hpp:744
::rect get_clip()
Get the current clipping area, in draw coordinates.
Definition: draw.cpp:468
config generate_report(const std::string &name, context &ct, bool only_static=false)
Definition: reports.cpp:1771
void update_tod(const time_of_day *tod_override=nullptr)
Applies r,g,b coloring to the map.
Definition: display.cpp:438
void update_fps_label()
Definition: display.cpp:1346
uint8_t alpha_mod
An alpha modifier to apply when drawing.
Definition: display.hpp:974
rect intersect(const SDL_Rect &r) const
Calculates the intersection of this rectangle and another; that is, the maximal rectangle that is con...
Definition: rect.cpp:92
std::string background_image
Definition: theme.hpp:92
SDL_Rect minimap_location_
Definition: display.hpp:758
pango_text & set_characters_per_line(const unsigned characters_per_line)
Definition: text.cpp:423
The basic class for representing 8-bit RGB or RGBA colour values.
Definition: color.hpp:62
double size
Definition: theme.hpp:90
void set_zoom(unsigned int amount)
Sets the scaling factor for images.
Definition: picture.cpp:760
TERRAIN_TYPE
Used as a parameter for the get_terrain_at function.
Definition: builder.hpp:52
std::string grid_bottom
bool headless()
The game is running headless.
Definition: video.cpp:107
tod_color color_adjust_
Definition: display.hpp:1134
void reinit_flags_for_team(const team &)
Rebuild the flag list (not team colors) for a single side.
Definition: display.cpp:308
fake_unit_manager * fake_units
Definition: resources.cpp:31
bool is_enemy(int n) const
Definition: team.hpp:231
void draw_text_in_hex(const map_location &loc, const drawing_layer layer, const std::string &text, std::size_t font_size, color_t color, double x_in_hex=0.5, double y_in_hex=0.5)
Draw text on a hex.
Definition: display.cpp:1483
void bounds_check_position()
Definition: display.cpp:2307
Modify, read and display user preferences.
texture back_
Definition: display.hpp:604
bool font_rgb_set() const
Definition: theme.hpp:140
bool invalidateGameStatus_
Definition: display.hpp:762
#define MinZoom
Definition: display.cpp:86
void set_position(double xpos, double ypos)
void update_arrow(arrow &a)
Called by arrow objects when they change.
Definition: display.cpp:3366
map_display and display: classes which take care of displaying the map and game-data on the screen...
unsigned int fps_actual_
Definition: display.hpp:772
virtual const unit_map & units() const =0
bool animate_map()
Definition: general.cpp:818
std::string shroud_prefix
Definition: game_config.cpp:72
void create_buttons()
Definition: display.cpp:896
const std::string & get_id() const
Definition: theme.hpp:54
const color_t YELLOW_COLOR
void update_render_textures()
Ensure render textures are valid and correct.
Definition: display.cpp:2662
const rect_of_hexes hexes_under_rect(const SDL_Rect &r) const
Return the rectangular area of hexes overlapped by r (r is in screen coordinates) ...
Definition: display.cpp:678
void swap(config &lhs, config &rhs)
Implement non-member swap function for std::swap (calls config::swap).
Definition: config.cpp:1456
void invalidate_all()
Function to invalidate all tiles.
Definition: display.cpp:3221
void layout_buttons()
Definition: display.cpp:846
void set_draw_size(int w, int h)
Set the intended size of the texture, in draw-space.
Definition: texture.hpp:131
int invalidated_hexes_
Count work done for the debug info displayed under fps.
Definition: display.hpp:1117
bool draw_terrain_codes_
Debug flag - overlay terrain codes on tiles.
Definition: display.hpp:1125
bool overlaps(const SDL_Rect &r) const
Whether the given rectangle and this rectangle overlap.
Definition: rect.cpp:74
void pump()
Process all events currently in the queue.
Definition: events.cpp:475
const color_t NORMAL_COLOR
bool fogged(const map_location &loc) const
Returns true if location (x,y) is covered in fog.
Definition: display.cpp:735
pango_text & set_family_class(font::family_class fclass)
Definition: text.cpp:366
Generic locator abstracting the location of an image.
Definition: picture.hpp:62
void render(const rect &r)
Render halos in region.
Definition: halo.cpp:451
static int hex_width()
Function which returns the width of a hex in pixels, up to where the next hex starts.
Definition: display.hpp:246
std::string flag_rgb
static unsigned int last_zoom_
The previous value of zoom_.
Definition: display.hpp:754
Fog and shroud.
Definition: display.hpp:848
void set_color_adjustment(int r, int g, int b)
Changes Time of Day color tint for all applicable image types.
Definition: picture.cpp:747
bool empty() const
False if both w and h are > 0, true otherwise.
Definition: rect.cpp:49
handle add(int x, int y, const std::string &image, const map_location &loc, halo::ORIENTATION orientation=NORMAL, bool infinite=true)
Add a haloing effect using &#39;image centered on (x,y).
Definition: halo.cpp:426
virtual rect get_clip_rect() const
Get the clipping rectangle for drawing.
Definition: display.cpp:2733
virtual rect screen_location() override
Return the current draw location of the display, on the screen.
Definition: display.cpp:2654
Encapsulates the map of the game.
Definition: location.hpp:38
void set_color(const color_t &color)
void set_team(const team *)
Definition: label.cpp:139
bool shrouded(const map_location &loc) const
Returns true if location (x,y) is covered in shroud.
Definition: display.cpp:730
boost::iterator_range< const_child_iterator > const_child_itors
Definition: config.hpp:205
blit_helper & drawing_buffer_add(const drawing_layer layer, const map_location &loc, const SDL_Rect &dest, const texture &tex)
Add an item to the drawing buffer.
Definition: display.cpp:1212
unit_iterator find(std::size_t id)
Definition: map.cpp:310
std::unique_ptr< std::ostream > scoped_ostream
Definition: filesystem.hpp:40
void tiled(const texture &tex, const SDL_Rect &dst, bool centered=false, bool mirrored=false)
Tile a texture to fill a region.
Definition: draw.cpp:360
void process_reachmap_changes()
Definition: display.cpp:3400
const border_t & border() const
Definition: theme.hpp:282
void update_fps_count()
Definition: display.cpp:1667
clip_setter reduce_clip(const SDL_Rect &clip)
Set the clipping area to the intersection of the current clipping area and the given rectangle...
Definition: draw.cpp:448
void recalculate_labels()
Definition: label.cpp:245
void set_theme(const std::string &new_theme)
Definition: display.cpp:281
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:215
bool draw_all_panels(const rect &region)
Redraws all panels intersecting the given region.
Definition: display.cpp:1461
virtual overlay_map & get_overlays()=0
std::size_t i
Definition: function.cpp:967
virtual const std::vector< team > & teams() const =0
bool is_blindfolded() const
Definition: display.cpp:510
void fade_tod_mask(const std::string &old, const std::string &new_)
ToD mask smooth fade.
Definition: display.cpp:2365
bool show_border
Definition: theme.hpp:95
void scroll_to_tile(const map_location &loc, SCROLL_TYPE scroll_type=ONSCREEN, bool check_fogged=true, bool force=true)
Scroll such that location loc is on-screen.
Definition: display.cpp:2154
void announce(const std::string &msg, const color_t &color=font::GOOD_COLOR, const announce_options &options=announce_options())
Announce a message prominently.
Definition: display.cpp:1721
virtual void highlight_hex(map_location hex)
Definition: display.cpp:1639
Holds options for calls to function &#39;announce&#39; (announce).
Definition: display.hpp:621
std::size_t activeTeam_
Definition: display.hpp:891
const std::string & postfix() const
Definition: theme.hpp:133
std::vector< std::shared_ptr< gui::button > > menu_buttons_
Definition: display.hpp:779
unsigned int tile_size()
Definition: general.cpp:658
mock_party p
void set_diagnostic(const std::string &msg)
Definition: display.cpp:1649
void scroll_to_tiles(map_location loc1, map_location loc2, SCROLL_TYPE scroll_type=ONSCREEN, bool check_fogged=true, double add_spacing=0.0, bool force=true)
Scroll such that location loc1 is on-screen.
Definition: display.cpp:2166
void invalidate_animations_location(const map_location &loc)
Per-location invalidation called by invalidate_animations() Extra game per-location invalidation (vil...
Definition: display.cpp:3296
bool hflip
Whether to mirror horizontally on draw.
Definition: display.hpp:970
default layer for drawing units
Definition: display.hpp:830
map_location get_middle_location() const
Definition: display.cpp:3377
static const std::array layer_groups
Definition: display.hpp:929
static map_location::DIRECTION s
pango_text & set_font_size(unsigned font_size)
Definition: text.cpp:376
std::vector< std::string > names
Definition: build_info.cpp:67
unsigned int fps_counter_
Definition: display.hpp:770
virtual void update() override
Update animations and internal state.
Definition: display.cpp:2531
Definition: theme.hpp:42
virtual ~display()
Definition: display.cpp:271
double g
Definition: astarsearch.cpp:65
color_t font_rgb() const
Definition: theme.hpp:139
std::size_t playing_team() const
The playing team is the team whose turn it is.
Definition: display.hpp:106
bool draw_num_of_bitmaps_
Debug flag - overlay number of bitmaps on tiles.
Definition: display.hpp:1127
bool redraw_background_
Definition: display.hpp:759
void read(const config &cfg)
Definition: display.cpp:3392
bool dont_show_all_
Definition: display.hpp:737
int get_location_y(const map_location &loc) const
Definition: display.cpp:745
virtual void render() override
Update offscreen render buffers.
Definition: display.cpp:2579
std::basic_string< signed char > light_string
Type used to store color information of central and adjacent hexes.
Definition: picture.hpp:182
void start_animation(int start_time, bool cycles=false)
Starts an animation cycle.
Helper structure for rendering the terrains.
Definition: display.hpp:944
rect pango_draw_text(bool actually_draw, const rect &area, int size, const color_t &color, const std::string &text, int x, int y, bool use_tooltips, pango_text::FONT_STYLE style)
Draws text on the screen.
void render_image(int x, int y, const display::drawing_layer drawing_layer, const map_location &loc, const image::locator &i_locator, bool hreverse=false, bool greyscale=false, uint8_t alpha=SDL_ALPHA_OPAQUE, double highlight=0.0, color_t blendto={0, 0, 0}, double blend_ratio=0, double submerged=0.0, bool vreverse=false)
Draw an image at a certain location.
Definition: display.cpp:1562
void render_map_outside_area()
Draw/redraw the off-map background area.
Definition: display.cpp:2707
An abstract description of a rectangle with integer coordinates.
Definition: rect.hpp:46
int add_floating_label(const floating_label &flabel)
add a label floating on the screen above everything else.
const std::string & image() const
Definition: theme.hpp:157
virtual void layout() override
Finalize screen layout.
Definition: display.cpp:2548
bool tile_nearly_on_screen(const map_location &loc) const
Checks if location loc or one of the adjacent tiles is visible on screen.
Definition: display.cpp:2062
Holds a 2D point.
Definition: point.hpp:24
bool invalidate_locations_in_rect(const SDL_Rect &rect)
invalidate all hexes under the rectangle rect (in screen coordinates)
Definition: display.cpp:3281
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:385
Layer for the terrain drawn in front of the unit.
Definition: display.hpp:831
bool add_exclusive_draw(const map_location &loc, unit &unit)
Allows a unit to request to be the only one drawn in its hex.
Definition: display.cpp:410
const rect & palette_area() const
Definition: display.cpp:520
virtual void draw_hex(const map_location &loc)
Redraws a single gamemap location.
Definition: display.cpp:2776
Declarations for File-IO.
Represents terrains which are to be drawn in front of them.
Definition: builder.hpp:57
bool is_zero() const
Definition: time_of_day.hpp:36
static bool zoom_at_min()
Definition: display.cpp:1974
int w
const menu * get_menu_item(const std::string &key) const
Definition: theme.cpp:918
Used for the bottom half part of grid image.
Definition: display.hpp:835
theme theme_
Definition: display.hpp:746
const bool & debug
bool view_locked_
Definition: display.hpp:745
const std::vector< action > & actions() const
Definition: theme.hpp:258
const std::string & get_filename() const
Definition: picture.hpp:81
Definitions for the terrain builder.
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:72
Represents terrains which are to be drawn behind unit sprites.
Definition: builder.hpp:53
Layer for the terrain drawn behind the unit.
Definition: display.hpp:820
void draw_label(const theme::label &label)
Definition: display.cpp:1434
static int get_zoom_levels_index(unsigned int zoom_level)
Definition: display.cpp:103
config & add_child(config_key_type key)
Definition: config.cpp:514
void blit(const texture &tex, const SDL_Rect &dst)
Draws a texture, or part of a texture, at the given location.
Definition: draw.cpp:301
drawing_buffer_key(const map_location &loc, drawing_layer layer)
Definition: display.cpp:1250
std::vector< std::string > fog_images_
Definition: display.hpp:788
Text class.
Definition: text.hpp:76
std::chrono::seconds fps_start_
Definition: display.hpp:771
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:75
void reset()
Releases ownership of the managed texture and resets the ptr to null.
Definition: texture.cpp:208
void set_team(std::size_t team, bool observe=false)
Sets the team controlled by the player using the computer.
Definition: display.cpp:386
bool grid()
Definition: general.cpp:558
const status_item * get_status_item(const std::string &item) const
Definition: theme.cpp:909
std::map< map_location, unsigned int > reach_map
Definition: display.hpp:1103
bool set_resolution(const SDL_Rect &screen)
Definition: theme.cpp:605
virtual rect & location(const SDL_Rect &screen) const
Definition: theme.cpp:318
void draw_report(const std::string &report_name, bool test_run=false)
Draw the specified report.
Definition: display.cpp:3047
Definition: display.hpp:45
texture tod_hex_mask1
Definition: display.hpp:784
const std::vector< team > & get_teams() const
Definition: display.hpp:103
static color_t get_minimap_color(int side)
Definition: team.cpp:962
reach_map reach_map_old_
Definition: display.hpp:1105
int get_location_x(const map_location &loc) const
Functions to get the on-screen positions of hexes.
Definition: display.cpp:740
const gamemap & get_map() const
Definition: display.hpp:101
const std::string & color() const
Definition: team.hpp:244
events::mouse_handler & get_mouse_handler_base() override
Get a reference to a mouse handler member a derived class uses.
exclusive_unit_draw_requests_t exclusive_unit_draw_requests_
map of hexes where only one unit should be drawn, the one identified by the associated id string ...
Definition: display.hpp:699
bool invalidateAll_
Definition: display.hpp:760
void redraw_unit(const unit &u) const
draw a unit.
Definition: drawer.cpp:104
texture pango_render_text(const std::string &text, int size, const color_t &color, font::pango_text::FONT_STYLE style, bool use_markup, int max_width)
Returns a SDL texture containing the rendered text.
events::generic_event scroll_event_
Event raised when the map is being scrolled.
Definition: display.hpp:767
iterator end() const
Definition: display.cpp:673
texture get_lighted_texture(const image::locator &i_locator, const light_string &ls)
Definition: picture.cpp:934
#define f
int lifetime
Lifetime measured in milliseconds.
Definition: display.hpp:624
double t
Definition: astarsearch.cpp:65
void reload_map()
Updates internals that cache map size.
Definition: display.cpp:490
color_t rep() const
High-contrast shade, intended for the minimap markers.
Definition: color_range.hpp:95
orb_status unit_orb_status(const unit &u) const
Returns an enumurated summary of whether this unit can move and/or attack.
std::string tile_image
Definition: theme.hpp:93
constexpr const SDL_Rect empty_rect
Definition: rect.hpp:30
std::set< map_location > invalidated_
Definition: display.hpp:780
std::vector< std::string > split(const config_attribute_value &val)
unsigned int tile_size
Definition: game_config.cpp:69
void toggle_default_zoom()
Sets the zoom amount to the default.
Definition: display.cpp:2043
void drawing_buffer_clear()
Clears the drawing buffer.
Definition: display.cpp:1325
std::map< std::string, config > reports_
Definition: display.hpp:778
const display_context * dc_
Definition: display.hpp:693
std::size_t viewing_team() const
The viewing team is the team currently viewing the game.
Definition: display.hpp:111
void flipped(const texture &tex, const SDL_Rect &dst, bool flip_h=true, bool flip_v=false)
Draws a texture, or part of a texture, at the given location, also mirroring/flipping the texture hor...
Definition: draw.cpp:331
uint8_t tod_hex_alpha1
Definition: display.hpp:786
const color_range & color_info(const std::string &name)
Functions to load and save images from/to disk.
int drawn_hexes_
Definition: display.hpp:1118
Standard logging facilities (interface).
int diagnostic_label_
Definition: display.hpp:761
void add_redraw_observer(std::function< void(display &)> f)
Adds a redraw observer, a function object to be called when a full rerender is queued.
Definition: display.cpp:2485
bool turbo()
Definition: general.cpp:468
std::string ellipsis
std::size_t font_size() const
Definition: theme.hpp:138
rect map_outside_area() const
Returns the available area for a map, this may differ from the above.
Definition: display.cpp:573
bool scroll_to_action()
Definition: general.cpp:385
A RAII object to temporary leave the synced context like in wesnoth.synchronize_choice.
color_t fade_color_
Definition: display.hpp:579
const int SIZE_FLOAT_LABEL
Definition: constants.cpp:32
map_labels & labels()
Definition: display.cpp:2723
const std::vector< menu > & menus() const
Definition: theme.hpp:256
#define e
static void fill_images_list(const std::string &prefix, std::vector< std::string > &images)
Definition: display.cpp:457
const std::unique_ptr< fake_unit_manager > fake_unit_man_
Definition: display.hpp:755
#define SmallZoom
Definition: display.cpp:85
#define final_zoom_index
Definition: display.cpp:83
std::vector< animated< image::locator > > flags_
Animated flags for each team.
Definition: display.hpp:806
const config & child_or_empty(config_key_type key) const
Returns the first child with the given key, or an empty config if there is none.
Definition: config.cpp:465
pango_text & set_maximum_height(int height, bool multiline)
Definition: text.cpp:434
#define zoom_levels
Definition: display.cpp:82
int get_end_time() const
std::string team_name
Definition: overlay.hpp:57
int side() const
Definition: team.hpp:176
virtual const time_of_day & get_time_of_day(const map_location &loc=map_location::null_location()) const
Definition: display.cpp:432
#define WRN_DP
Definition: display.cpp:77
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:60
texture get_texture(const image::locator &i_locator, TYPE type, bool skip_cache)
Returns an image texture suitable for hardware-accelerated rendering.
Definition: picture.cpp:1134
std::vector< std::tuple< int, int, int > > fps_history_
Definition: display.hpp:1136
mock_char c
void remove_single_overlay(const map_location &loc, const std::string &toDelete)
remove_single_overlay will remove a single overlay from a tile
Definition: display.cpp:160
pango_text & set_foreground_color(const color_t &color)
Definition: text.cpp:398
const rect & unit_image_area() const
Definition: display.cpp:525
static map_location::DIRECTION n
std::shared_ptr< halo_record > handle
Definition: halo.hpp:31
void add_overlay(const map_location &loc, const std::string &image, const std::string &halo="", const std::string &team_name="", const std::string &item_id="", bool visible_under_fog=true, float submerge=0.0f, float z_order=0)
Functions to add and remove overlays from locations.
Definition: display.cpp:142
int h() const
Effective map height.
Definition: map.hpp:53
bool scroll(int xmov, int ymov, bool force=false)
Scrolls the display by xmov,ymov pixels.
Definition: display.cpp:1869
void fade_to(const color_t &color, int duration)
Screen fade.
Definition: display.cpp:2388
void add_arrow(arrow &)
Definition: display.cpp:3352
void remove_overlay(const map_location &loc)
remove_overlay will remove all overlays on a tile.
Definition: display.cpp:155
bool animate_map_
Local cache for preferences::animate_map, since it is constantly queried.
Definition: display.hpp:796
Transitional API for porting SDL_ttf-based code to Pango.
void fill(const SDL_Rect &rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
Fill an area with the given colour.
Definition: draw.cpp:41
static rng & default_instance()
Definition: random.cpp:74
#define LOG_DP
Definition: display.cpp:78
const std::vector< panel > & panels() const
Definition: theme.hpp:254
static void toggle_debug_foreground()
Toggle to debug foreground terrain.
Definition: display.cpp:1335
std::string grid_top
const rect & main_map_location(const SDL_Rect &screen) const
Definition: theme.hpp:273
const std::unique_ptr< terrain_builder > builder_
Definition: display.hpp:756
texture front_
Render textures, for intermediate rendering.
Definition: display.hpp:603
void draw()
Trigger a draw cycle.
Definition: events.cpp:740
static const std::string & get_variant(const std::vector< std::string > &variants, const map_location &loc)
Definition: display.cpp:479
void invalidate_region(const rect &region)
Mark a region of the screen as requiring redraw.
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
std::string fog_prefix
Definition: game_config.cpp:72
bool empty() const
Definition: config.cpp:941
void refresh_report(const std::string &report_name, const config *new_cfg=nullptr)
Update the given report.
Definition: display.cpp:2977
std::weak_ptr< wb::manager > wb_
Definition: display.hpp:695
std::shared_ptr< gui::button > find_menu_button(const std::string &id)
Definition: display.cpp:836
Definition: display.hpp:49
int blindfold_ctr_
Definition: display.hpp:689
const std::string & team_name() const
Definition: team.hpp:284
static void add_submerge_ipf_mod(std::string &image_path, int image_height, double submersion_amount, int shift=0)
Definition: display.cpp:1516
TERRAIN_TYPE
Definition: display.hpp:722
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::string remove_exclusive_draw(const map_location &loc)
Cancels an exclusive draw request.
Definition: display.cpp:420
const int SIZE_SMALL
Definition: constants.cpp:24
std::pair< std::string, unsigned > item
Definition: help_impl.hpp:414
const color_t BAD_COLOR
std::size_t currentTeam_
Definition: display.hpp:736
void rebuild_all()
Rebuild all dynamic terrain.
Definition: display.cpp:485
bool propagate_invalidation(const std::set< map_location > &locs)
If this set is partially invalidated, invalidate all its hexes.
Definition: display.cpp:3249
void write(config &cfg) const
Definition: location.cpp:212