The Battle for Wesnoth  1.15.0-dev
display.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * Routines to set up the display, scroll and zoom the map.
18  */
19 
20 #include "arrow.hpp"
21 #include "cursor.hpp"
22 #include "display.hpp"
23 #include "fake_unit_manager.hpp"
24 #include "font/sdl_ttf.hpp"
25 #include "font/text.hpp"
26 #include "preferences/game.hpp"
27 #include "gettext.hpp"
28 #include "halo.hpp"
30 #include "language.hpp"
31 #include "log.hpp"
32 #include "font/marked-up_text.hpp"
33 #include "map/map.hpp"
34 #include "map/label.hpp"
35 #include "minimap.hpp"
36 #include "overlay.hpp"
37 #include "play_controller.hpp" //note: this can probably be refactored out
38 #include "reports.hpp"
39 #include "resources.hpp"
40 #include "color.hpp"
41 #include "synced_context.hpp"
42 #include "team.hpp"
43 #include "terrain/builder.hpp"
44 #include "time_of_day.hpp"
45 #include "tooltips.hpp"
46 #include "tod_manager.hpp"
47 #include "units/unit.hpp"
49 #include "units/drawer.hpp"
50 #include "whiteboard/manager.hpp"
51 #include "show_dialog.hpp"
53 
54 #include <SDL_image.h>
55 
56 #include <algorithm>
57 #include <array>
58 #include <cmath>
59 #include <iomanip>
60 #include <utility>
61 
62 #ifdef _WIN32
63 #include <windows.h>
64 #endif
65 
66 // Includes for bug #17573
67 
68 static lg::log_domain log_display("display");
69 #define ERR_DP LOG_STREAM(err, log_display)
70 #define LOG_DP LOG_STREAM(info, log_display)
71 #define DBG_DP LOG_STREAM(debug, log_display)
72 
73 // These are macros instead of proper constants so that they auto-update if the game config is reloaded.
74 #define zoom_levels (game_config::zoom_levels)
75 #define final_zoom_index (static_cast<int>(zoom_levels.size()) - 1)
76 #define DefaultZoom (game_config::tile_size)
77 #define SmallZoom (DefaultZoom / 2)
78 #define MinZoom (zoom_levels.front())
79 #define MaxZoom (zoom_levels.back())
80 
81 namespace {
82  bool benchmark = false;
83 
84  bool debug_foreground = false;
85 
86  int prevLabel = 0;
87 }
88 
89 unsigned int display::zoom_ = DefaultZoom;
90 unsigned int display::last_zoom_ = SmallZoom;
91 
93 {
94  const team& curr_team = dc_->teams()[playing_team()];
95  const team& prev_team = playing_team() == 0
96  ? dc_->teams().back()
98  for (const auto& i : get_overlays()) {
99  for(const overlay& ov : i.second) {
100  if(!ov.team_name.empty() &&
101  ((ov.team_name.find(curr_team.team_name()) + 1) != 0) !=
102  ((ov.team_name.find(prev_team.team_name()) + 1) != 0))
103  {
104  invalidate(i.first);
105  }
106  }
107  }
108 }
109 
110 
111 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 z_order)
112 {
113  if (halo_man_) {
114  halo::handle halo_handle;
115  if(halo != "") {
116  halo_handle = halo_man_->add(get_location_x(loc) + hex_size() / 2,
117  get_location_y(loc) + hex_size() / 2, halo, loc);
118  }
119 
120  std::vector<overlay>& overlays = get_overlays()[loc];
121  auto it = std::find_if(overlays.begin(), overlays.end(), [z_order](const overlay& ov) { return ov.z_order > z_order; });
122  overlays.emplace(it, img, halo, halo_handle, team_name, item_id, visible_under_fog, z_order);
123  }
124 }
125 
127 {
128  get_overlays().erase(loc);
129 }
130 
131 void display::remove_single_overlay(const map_location& loc, const std::string& toDelete)
132 {
133  std::vector<overlay>& overlays = get_overlays()[loc];
134  overlays.erase(
135  std::remove_if(
136  overlays.begin(), overlays.end(),
137  [&toDelete](const overlay& ov) { return ov.image == toDelete || ov.halo == toDelete || ov.id == toDelete; }
138  ),
139  overlays.end()
140  );
141 }
142 
143 display::display(const display_context * dc, std::weak_ptr<wb::manager> wb, reports & reports_object, const config& theme_cfg, const config& level, bool auto_join) :
144  video2::draw_layering(auto_join),
145  dc_(dc),
146  halo_man_(new halo::manager(*this)),
147  wb_(wb),
150  currentTeam_(0),
151  dont_show_all_(false),
152  xpos_(0),
153  ypos_(0),
154  view_locked_(false),
155  theme_(theme_cfg, screen_.screen_area()),
156  zoom_index_(0),
157  fake_unit_man_(new fake_unit_manager(*this)),
158  builder_(new terrain_builder(level, (dc_ ? &dc_->map() : nullptr), theme_.border().tile_image, theme_.border().show_border)),
159  minimap_(nullptr),
161  redrawMinimap_(false),
162  redraw_background_(true),
163  invalidateAll_(true),
164  grid_(false),
166  panelsDrawn_(false),
167  turbo_speed_(2),
168  turbo_(false),
169  invalidateGameStatus_(true),
170  map_labels_(new map_labels(nullptr)),
171  reports_object_(&reports_object),
172  scroll_event_("scrolled"),
173  complete_redraw_event_("completely_redrawn"),
174  frametimes_(50),
175  fps_counter_(),
176  fps_start_(),
177  fps_actual_(),
178  reportRects_(),
179  reportSurfaces_(),
180  reports_(),
181  menu_buttons_(),
182  action_buttons_(),
183  invalidated_(),
184  mouseover_hex_overlay_(nullptr),
185  tod_hex_mask1(nullptr),
186  tod_hex_mask2(nullptr),
187  fog_images_(),
188  shroud_images_(),
189  selectedHex_(),
190  mouseoverHex_(),
191  keys_(),
192  animate_map_(true),
193  animate_water_(true),
194  flags_(),
195  activeTeam_(0),
196  drawing_buffer_(),
197  map_screenshot_(false),
198  reach_map_(),
199  reach_map_old_(),
200  reach_map_changed_(true),
201  fps_handle_(0),
203  drawn_hexes_(0),
205  idle_anim_rate_(1.0),
206  map_screenshot_surf_(nullptr),
208  draw_coordinates_(false),
209  draw_terrain_codes_(false),
210  draw_num_of_bitmaps_(false),
211  arrows_map_(),
212  color_adjust_(),
213  dirty_()
214 {
215  //The following assertion fails when starting a campaign
216  assert(singleton_ == nullptr);
217  singleton_ = this;
218 
220 
221  blindfold_ctr_ = 0;
222 
223  read(level.child_or_empty("display"));
224 
226  && (screen_.getSurface() != nullptr
227  && screen_.faked())) {
228  screen_.lock_updates(true);
229  }
230 
233 
235 
236  zoom_index_ = std::distance(zoom_levels.begin(), std::find(zoom_levels.begin(), zoom_levels.end(), zoom_));
237 
239 
240  init_flags();
241 
242  if(!menu_buttons_.empty() || !action_buttons_.empty()) {
243  create_buttons();
244  }
245 
246 #ifdef _WIN32
247  // Increase timer resolution to prevent delays getting much longer than they should.
248  timeBeginPeriod(1u);
249 #endif
250 }
251 
253 {
254 #ifdef _WIN32
255  timeEndPeriod(1u);
256 #endif
257 
258  singleton_ = nullptr;
259  resources::fake_units = nullptr;
260 }
261 
262 void display::set_theme(config theme_cfg) {
263  theme_ = theme(theme_cfg, screen_.screen_area());
264  builder_->set_draw_border(theme_.border().show_border);
265  menu_buttons_.clear();
266  action_buttons_.clear();
267  create_buttons();
269  rebuild_all();
271 }
272 
274 
275  flags_.clear();
276  if (!dc_) return;
277  flags_.resize(dc_->teams().size());
278 
279  std::vector<std::string> side_colors;
280  side_colors.reserve(dc_->teams().size());
281 
282  for(const team& t : dc_->teams()) {
283  std::string side_color = t.color();
284  side_colors.push_back(side_color);
285  init_flags_for_side_internal(t.side() - 1, side_color);
286  }
287  image::set_team_colors(&side_colors);
288 }
289 
290 void display::reinit_flags_for_side(std::size_t side)
291 {
292  if (!dc_ || side >= dc_->teams().size()) {
293  ERR_DP << "Cannot rebuild flags for inexistent or unconfigured side " << side << '\n';
294  return;
295  }
296 
297  init_flags_for_side_internal(side, dc_->teams()[side].color());
298 }
299 
300 void display::init_flags_for_side_internal(std::size_t n, const std::string& side_color)
301 {
302  assert(dc_ != nullptr);
303  assert(n < dc_->teams().size());
304  assert(n < flags_.size());
305 
306  std::string flag = dc_->teams()[n].flag();
307  std::string old_rgb = game_config::flag_rgb;
308  std::string new_rgb = side_color;
309 
310  if(flag.empty()) {
312  }
313 
314  LOG_DP << "Adding flag for team " << n << " from animation " << flag << "\n";
315 
316  // Must recolor flag image
317  animated<image::locator> temp_anim;
318 
319  std::vector<std::string> items = utils::square_parenthetical_split(flag);
320 
321  for(const std::string& item : items) {
322  const std::vector<std::string>& sub_items = utils::split(item, ':');
323  std::string str = item;
324  int time = 100;
325 
326  if(sub_items.size() > 1) {
327  str = sub_items.front();
328  try {
329  time = std::max<int>(1, std::stoi(sub_items.back()));
330  } catch(const std::invalid_argument&) {
331  ERR_DP << "Invalid time value found when constructing flag for side " << n << ": " << sub_items.back() << "\n";
332  }
333  }
334 
335  std::stringstream temp;
336  temp << str << "~RC(" << old_rgb << ">"<< new_rgb << ")";
337  image::locator flag_image(temp.str());
338  temp_anim.add_frame(time, flag_image);
339  }
340 
342  f = temp_anim;
343  auto time = f.get_end_time();
344  if (time > 0) {
345  f.start_animation(randomness::rng::default_instance().get_random_int(0, time-1), true);
346  }
347  else {
348  // this can happen if both flag and game_config::images::flag are empty.
349  ERR_DP << "missing flag for team" << n << "\n";
350  }
351 }
352 
354 {
355  if(!get_map().is_village(loc)) {
356  return surface(nullptr);
357  }
358 
359  for (const team& t : dc_->teams()) {
360  if (t.owns_village(loc) && (!fogged(loc) || !dc_->get_team(viewing_side()).is_enemy(t.side())))
361  {
362  auto& flag = flags_[t.side() - 1];
363  flag.update_last_draw_time();
364  const image::locator &image_flag = animate_map_ ?
365  flag.get_current_frame() : flag.get_first_frame();
366  return image::get_image(image_flag, image::TOD_COLORED);
367  }
368  }
369 
370  return surface(nullptr);
371 }
372 
373 void display::set_team(std::size_t teamindex, bool show_everything)
374 {
375  assert(teamindex < dc_->teams().size());
376  currentTeam_ = teamindex;
377  if (!show_everything)
378  {
379  labels().set_team(&dc_->teams()[teamindex]);
380  dont_show_all_ = true;
381  }
382  else
383  {
384  labels().set_team(nullptr);
385  dont_show_all_ = false;
386  }
388  if(std::shared_ptr<wb::manager> w = wb_.lock())
389  w->on_viewer_change(teamindex);
390 }
391 
392 void display::set_playing_team(std::size_t teamindex)
393 {
394  assert(teamindex < dc_->teams().size());
395  activeTeam_ = teamindex;
397 }
398 
400 {
401  if (loc.valid() && exclusive_unit_draw_requests_.find(loc) == exclusive_unit_draw_requests_.end())
402  {
403  exclusive_unit_draw_requests_[loc] = unit.id();
404  return true;
405  }
406  else
407  {
408  return false;
409  }
410 }
411 
413 {
414  std::string id = "";
415  if(loc.valid())
416  {
418  //id will be set to the default "" string by the [] operator if the map doesn't have anything for that loc.
420  }
421  return id;
422 }
423 
424 const time_of_day & display::get_time_of_day(const map_location& /*loc*/) const
425 {
426  static time_of_day tod;
427  return tod;
428 }
429 
430 void display::update_tod(const time_of_day* tod_override)
431 {
432  const time_of_day* tod = tod_override;
433  if(tod == nullptr) {
434  tod = &get_time_of_day();
435  }
436 
437  const tod_color col = color_adjust_ + tod->color;
438  image::set_color_adjustment(col.r, col.g, col.b);
439 }
440 
441 void display::adjust_color_overlay(int r, int g, int b)
442 {
443  color_adjust_ = tod_color(r, g, b);
444  update_tod();
445 }
446 
447 void display::fill_images_list(const std::string& prefix, std::vector<std::string>& images)
448 {
449  if(prefix == ""){
450  return;
451  }
452 
453  // search prefix.png, prefix1.png, prefix2.png ...
454  for(int i=0; ; ++i){
455  std::ostringstream s;
456  s << prefix;
457  if(i != 0)
458  s << i;
459  s << ".png";
460  if(image::exists(s.str()))
461  images.push_back(s.str());
462  else if(i>0)
463  break;
464  }
465  if (images.empty())
466  images.emplace_back();
467 }
468 
469 const std::string& display::get_variant(const std::vector<std::string>& variants, const map_location &loc)
470 {
471  //TODO use better noise function
472  return variants[std::abs(loc.x + loc.y) % variants.size()];
473 }
474 
476 {
477  builder_->rebuild_all();
478 }
479 
481 {
482  redraw_background_ = true;
483  builder_->reload_map();
484 }
485 
487 {
488  dc_ = dc;
489  builder_->change_map(&dc_->map()); //TODO: Should display_context own and initialize the builder object?
490 }
491 
493 {
494  halo_man_.reset(new halo::manager(*this));
495 }
496 
498 {
499  halo_man_.reset(&halo_man);
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 
515 
516 const SDL_Rect& display::max_map_area() const
517 {
518  static SDL_Rect max_area {0, 0, 0, 0};
519 
520  // hex_size() is always a multiple of 4
521  // and hex_width() a multiple of 3,
522  // so there shouldn't be off-by-one-errors
523  // due to rounding.
524  // To display a hex fully on screen,
525  // a little bit extra space is needed.
526  // Also added the border two times.
527  max_area.w = static_cast<int>((get_map().w() + 2 * theme_.border().size + 1.0/3.0) * hex_width());
528  max_area.h = static_cast<int>((get_map().h() + 2 * theme_.border().size + 0.5) * hex_size());
529 
530  return max_area;
531 }
532 
533 const SDL_Rect& display::map_area() const
534 {
535  static SDL_Rect max_area;
536  max_area = max_map_area();
537 
538  // if it's for map_screenshot, maximize and don't recenter
539  if (map_screenshot_) {
540  return max_area;
541  }
542 
543  static SDL_Rect res;
544  res = map_outside_area();
545 
546  if(max_area.w < res.w) {
547  // map is smaller, center
548  res.x += (res.w - max_area.w)/2;
549  res.w = max_area.w;
550  }
551 
552  if(max_area.h < res.h) {
553  // map is smaller, center
554  res.y += (res.h - max_area.h)/2;
555  res.h = max_area.h;
556  }
557 
558  return res;
559 }
560 
561 bool display::outside_area(const SDL_Rect& area, const int x, const int y)
562 {
563  const int x_thresh = hex_size();
564  const int y_thresh = hex_size();
565  return (x < area.x || x > area.x + area.w - x_thresh ||
566  y < area.y || y > area.y + area.h - y_thresh);
567 }
568 
569 // This function uses the screen as reference
570 const map_location display::hex_clicked_on(int xclick, int yclick) const
571 {
572  const SDL_Rect& rect = map_area();
573  if(sdl::point_in_rect(xclick,yclick,rect) == false) {
574  return map_location();
575  }
576 
577  xclick -= rect.x;
578  yclick -= rect.y;
579 
580  return pixel_position_to_hex(xpos_ + xclick, ypos_ + yclick);
581 }
582 
583 
584 // This function uses the rect of map_area as reference
586 {
587  // adjust for the border
588  x -= static_cast<int>(theme_.border().size * hex_width());
589  y -= static_cast<int>(theme_.border().size * hex_size());
590  // The editor can modify the border and this will result in a negative y
591  // value. Instead of adding extra cases we just shift the hex. Since the
592  // editor doesn't use the direction this is no problem.
593  const int offset = y < 0 ? 1 : 0;
594  if(offset) {
595  x += hex_width();
596  y += hex_size();
597  }
598  const int s = hex_size();
599  const int tesselation_x_size = hex_width() * 2;
600  const int tesselation_y_size = s;
601  const int x_base = x / tesselation_x_size * 2;
602  const int x_mod = x % tesselation_x_size;
603  const int y_base = y / tesselation_y_size;
604  const int y_mod = y % tesselation_y_size;
605 
606  int x_modifier = 0;
607  int y_modifier = 0;
608 
609  if (y_mod < tesselation_y_size / 2) {
610  if ((x_mod * 2 + y_mod) < (s / 2)) {
611  x_modifier = -1;
612  y_modifier = -1;
613  } else if ((x_mod * 2 - y_mod) < (s * 3 / 2)) {
614  x_modifier = 0;
615  y_modifier = 0;
616  } else {
617  x_modifier = 1;
618  y_modifier = -1;
619  }
620 
621  } else {
622  if ((x_mod * 2 - (y_mod - s / 2)) < 0) {
623  x_modifier = -1;
624  y_modifier = 0;
625  } else if ((x_mod * 2 + (y_mod - s / 2)) < s * 2) {
626  x_modifier = 0;
627  y_modifier = 0;
628  } else {
629  x_modifier = 1;
630  y_modifier = 0;
631  }
632  }
633 
634  return map_location(x_base + x_modifier - offset, y_base + y_modifier - offset);
635 }
636 
638 {
639  if (loc_.y < rect_.bottom[loc_.x & 1])
640  ++loc_.y;
641  else {
642  ++loc_.x;
643  loc_.y = rect_.top[loc_.x & 1];
644  }
645 
646  return *this;
647 }
648 
649 // begin is top left, and end is after bottom right
651 {
652  return iterator(map_location(left, top[left & 1]), *this);
653 }
655 {
656  return iterator(map_location(right+1, top[(right+1) & 1]), *this);
657 }
658 
659 const display::rect_of_hexes display::hexes_under_rect(const SDL_Rect& r) const
660 {
661  rect_of_hexes res;
662 
663  if (r.w<=0 || r.h<=0) {
664  // empty rect, return dummy values giving begin=end
665  res.left = 0;
666  res.right = -1; // end is right+1
667  res.top[0] = 0;
668  res.top[1] = 0;
669  res.bottom[0] = 0;
670  res.bottom[1] = 0;
671  return res;
672  }
673 
674  SDL_Rect map_rect = map_area();
675  // translate rect coordinates from screen-based to map_area-based
676  int x = xpos_ - map_rect.x + r.x;
677  int y = ypos_ - map_rect.y + r.y;
678  // we use the "double" type to avoid important rounding error (size of an hex!)
679  // we will also need to use std::floor to avoid bad rounding at border (negative values)
680  double tile_width = hex_width();
681  double tile_size = hex_size();
682  double border = theme_.border().size;
683  // we minus "0.(3)", for horizontal imbrication.
684  // reason is: two adjacent hexes each overlap 1/4 of their width, so for
685  // grid calculation 3/4 of tile width is used, which by default gives
686  // 18/54=0.(3). Note that, while tile_width is zoom dependent, 0.(3) is not.
687  res.left = static_cast<int>(std::floor(-border + x / tile_width - 0.3333333));
688  // we remove 1 pixel of the rectangle dimensions
689  // (the rounded division take one pixel more than needed)
690  res.right = static_cast<int>(std::floor(-border + (x + r.w-1) / tile_width));
691 
692  // for odd x, we must shift up one half-hex. Since x will vary along the edge,
693  // we store here the y values for even and odd x, respectively
694  res.top[0] = static_cast<int>(std::floor(-border + y / tile_size));
695  res.top[1] = static_cast<int>(std::floor(-border + y / tile_size - 0.5));
696  res.bottom[0] = static_cast<int>(std::floor(-border + (y + r.h-1) / tile_size));
697  res.bottom[1] = static_cast<int>(std::floor(-border + (y + r.h-1) / tile_size - 0.5));
698 
699  // TODO: in some rare cases (1/16), a corner of the big rect is on a tile
700  // (the 72x72 rectangle containing the hex) but not on the hex itself
701  // Can maybe be optimized by using pixel_position_to_hex
702 
703  return res;
704 }
705 
707 {
708  return currentTeam_ < dc_->teams().size();
709 }
710 
711 bool display::shrouded(const map_location& loc) const
712 {
713  return is_blindfolded() || (dont_show_all_ && dc_->teams()[currentTeam_].shrouded(loc));
714 }
715 
716 bool display::fogged(const map_location& loc) const
717 {
718  return is_blindfolded() || (dont_show_all_ && dc_->teams()[currentTeam_].fogged(loc));
719 }
720 
722 {
723  return static_cast<int>(map_area().x + (loc.x + theme_.border().size) * hex_width() - xpos_);
724 }
725 
727 {
728  return static_cast<int>(map_area().y + (loc.y + theme_.border().size) * zoom_ - ypos_ + (is_odd(loc.x) ? zoom_/2 : 0));
729 }
730 
732 {
733  //TODO: don't return location for this,
734  // instead directly scroll to the clicked pixel position
735 
736  if (!sdl::point_in_rect(x, y, minimap_area())) {
737  return map_location();
738  }
739 
740  // we transform the coordinates from minimap to the full map image
741  // probably more adjustments to do (border, minimap shift...)
742  // but the mouse and human capacity to evaluate the rectangle center
743  // is not pixel precise.
744  int px = (x - minimap_location_.x) * get_map().w()*hex_width() / std::max (minimap_location_.w, 1);
745  int py = (y - minimap_location_.y) * get_map().h()*hex_size() / std::max(minimap_location_.h, 1);
746 
747  map_location loc = pixel_position_to_hex(px, py);
748  if (loc.x < 0)
749  loc.x = 0;
750  else if (loc.x >= get_map().w())
751  loc.x = get_map().w() - 1;
752 
753  if (loc.y < 0)
754  loc.y = 0;
755  else if (loc.y >= get_map().h())
756  loc.y = get_map().h() - 1;
757 
758  return loc;
759 }
760 
761 surface display::screenshot(bool map_screenshot)
762 {
763  if (!map_screenshot) {
764  // Use make_neutral_surface() to copy surface content
766  } else {
767  if (get_map().empty()) {
768  ERR_DP << "No map loaded, cannot create a map screenshot.\n";
769  return nullptr;
770  }
771 
772  SDL_Rect area = max_map_area();
774 
775  if (map_screenshot_surf_ == nullptr) {
776  // Memory problem ?
777  ERR_DP << "Could not create screenshot surface, try zooming out.\n";
778  return nullptr;
779  }
780 
781  // back up the current map view position and move to top-left
782  int old_xpos = xpos_;
783  int old_ypos = ypos_;
784  xpos_ = 0;
785  ypos_ = 0;
786 
787  // we reroute render output to the screenshot surface and invalidate all
788  map_screenshot_= true;
789  invalidateAll_ = true;
790  DBG_DP << "draw() with map_screenshot\n";
791  draw(true,true);
792 
793  // restore normal rendering
794  map_screenshot_= false;
795  xpos_ = old_xpos;
796  ypos_ = old_ypos;
797 
798  // Clear map_screenshot_surf_ and return a new surface that contains the same data
799  surface surf(std::move(map_screenshot_surf_));
800  return surf;
801  }
802 }
803 
804 std::shared_ptr<gui::button> display::find_action_button(const std::string& id)
805 {
806  for (std::size_t i = 0; i < action_buttons_.size(); ++i) {
807  if(action_buttons_[i]->id() == id) {
808  return action_buttons_[i];
809  }
810  }
811  return nullptr;
812 }
813 
814 std::shared_ptr<gui::button> display::find_menu_button(const std::string& id)
815 {
816  for (std::size_t i = 0; i < menu_buttons_.size(); ++i) {
817  if(menu_buttons_[i]->id() == id) {
818  return menu_buttons_[i];
819  }
820  }
821  return nullptr;
822 }
823 
825 {
826  DBG_DP << "positioning menu buttons...\n";
827  for(const auto& menu : theme_.menus()) {
828  std::shared_ptr<gui::button> b = find_menu_button(menu.get_id());
829  if(b) {
830  const SDL_Rect& loc = menu.location(screen_.screen_area());
831  b->set_location(loc);
832  b->set_measurements(0,0);
833  b->set_label(menu.title());
834  b->set_image(menu.image());
835  }
836  }
837 
838  DBG_DP << "positioning action buttons...\n";
839  for(const auto& action : theme_.actions()) {
840  std::shared_ptr<gui::button> b = find_action_button(action.get_id());
841  if(b) {
842  const SDL_Rect& loc = action.location(screen_.screen_area());
843  b->set_location(loc);
844  b->set_measurements(0,0);
845  b->set_label(action.title());
846  b->set_image(action.image());
847  }
848  }
849 }
850 
852 {
853  std::vector<std::shared_ptr<gui::button>> menu_work;
854  std::vector<std::shared_ptr<gui::button>> action_work;
855 
856  DBG_DP << "creating menu buttons...\n";
857  for(const auto& menu : theme_.menus()) {
858  if (!menu.is_button()) continue;
859 
860  std::shared_ptr<gui::button> b(new gui::button(screen_, menu.title(), gui::button::TYPE_PRESS, menu.image(),
861  gui::button::DEFAULT_SPACE, false, menu.overlay()));
862  DBG_DP << "drawing button " << menu.get_id() << "\n";
863  b->join_same(this);
864  b->set_id(menu.get_id());
865  if (!menu.tooltip().empty()){
866  b->set_tooltip_string(menu.tooltip());
867  }
868 
869  std::shared_ptr<gui::button> b_prev = find_menu_button(b->id());
870  if(b_prev) {
871  b->enable(b_prev->enabled());
872  }
873 
874  menu_work.push_back(b);
875  }
876 
877  DBG_DP << "creating action buttons...\n";
878  for(const auto& action : theme_.actions()) {
879  std::shared_ptr<gui::button> b(new gui::button(screen_, action.title(), string_to_button_type(action.type()), action.image(),
880  gui::button::DEFAULT_SPACE, false, action.overlay()));
881 
882  DBG_DP << "drawing button " << action.get_id() << "\n";
883  b->set_id(action.get_id());
884  b->join_same(this);
885  if (!action.tooltip(0).empty()){
886  b->set_tooltip_string(action.tooltip(0));
887  }
888 
889  std::shared_ptr<gui::button> b_prev = find_action_button(b->id());
890  if(b_prev) {
891  b->enable(b_prev->enabled());
892  if (b_prev->get_type() == gui::button::TYPE_CHECK) {
893  b->set_check(b_prev->checked());
894  }
895  }
896 
897  action_work.push_back(b);
898  }
899 
900 
901  menu_buttons_.clear();
902  menu_buttons_.assign(menu_work.begin(), menu_work.end());
903  action_buttons_.clear();
904  action_buttons_.assign(action_work.begin(), action_work.end());
905 
906  layout_buttons();
907  DBG_DP << "buttons created\n";
908 }
909 
911 {
912  for (std::shared_ptr<gui::button> btn : menu_buttons_) {
913  btn->set_dirty(true);
914  btn->draw();
915  }
916 
917  for (std::shared_ptr<gui::button> btn : action_buttons_) {
918  btn->set_dirty(true);
919  btn->draw();
920  }
921 }
922 
923 
925 {
927  if (type == "checkbox") { res = gui::button::TYPE_CHECK; }
928  else if (type == "image") { res = gui::button::TYPE_IMAGE; }
929  else if (type == "radiobox") { res = gui::button::TYPE_RADIO; }
930  else if (type == "turbo") { res = gui::button::TYPE_TURBO; }
931  return res;
932 }
933 
934 static const std::string& get_direction(std::size_t n)
935 {
936  static const std::array<std::string, 6> dirs {{ "-n", "-ne", "-se", "-s", "-sw", "-nw" }};
937  return dirs[n >= dirs.size() ? 0 : n];
938 }
939 
940 std::vector<surface> display::get_fog_shroud_images(const map_location& loc, image::TYPE image_type)
941 {
942  std::vector<std::string> names;
943 
944  adjacent_loc_array_t adjacent;
945  get_adjacent_tiles(loc, adjacent.data());
946 
947  enum visibility {FOG=0, SHROUD=1, CLEAR=2};
948  visibility tiles[6];
949 
950  const std::string* image_prefix[] =
952 
953  for(int i = 0; i < 6; ++i) {
954  if(shrouded(adjacent[i])) {
955  tiles[i] = SHROUD;
956  } else if(!fogged(loc) && fogged(adjacent[i])) {
957  tiles[i] = FOG;
958  } else {
959  tiles[i] = CLEAR;
960  }
961  }
962 
963  for(int v = FOG; v != CLEAR; ++v) {
964  // Find somewhere that doesn't have overlap to use as a starting point
965  int start;
966  for(start = 0; start != 6; ++start) {
967  if(tiles[start] != v) {
968  break;
969  }
970  }
971 
972  if(start == 6) {
973  // Completely surrounded by fog or shroud. This might have
974  // a special graphic.
975  const std::string name = *image_prefix[v] + "-all.png";
976  if ( image::exists(name) ) {
977  names.push_back(name);
978  // Proceed to the next visibility (fog -> shroud -> clear).
979  continue;
980  }
981  // No special graphic found. We'll just combine some other images
982  // and hope it works out.
983  start = 0;
984  }
985 
986  // Find all the directions overlap occurs from
987  for (int i = (start+1)%6, cap1 = 0; i != start && cap1 != 6; ++cap1) {
988  if(tiles[i] == v) {
989  std::ostringstream stream;
990  std::string name;
991  stream << *image_prefix[v];
992 
993  for (int cap2 = 0; v == tiles[i] && cap2 != 6; i = (i+1)%6, ++cap2) {
994  stream << get_direction(i);
995 
996  if(!image::exists(stream.str() + ".png")) {
997  // If we don't have any surface at all,
998  // then move onto the next overlapped area
999  if(name.empty()) {
1000  i = (i+1)%6;
1001  }
1002  break;
1003  } else {
1004  name = stream.str();
1005  }
1006  }
1007 
1008  if(!name.empty()) {
1009  names.push_back(name + ".png");
1010  }
1011  } else {
1012  i = (i+1)%6;
1013  }
1014  }
1015  }
1016 
1017  // now get the surfaces
1018  std::vector<surface> res;
1019 
1020  for (std::string& name : names) {
1021  surface surf(image::get_image(name, image_type));
1022  if (surf)
1023  res.push_back(std::move(surf));
1024  }
1025 
1026  return res;
1027 }
1028 
1030  const std::string& timeid,
1032 {
1033  terrain_image_vector_.clear();
1034 
1035  terrain_builder::TERRAIN_TYPE builder_terrain_type =
1036  (terrain_type == FOREGROUND ?
1038 
1039  const terrain_builder::imagelist* const terrains = builder_->get_terrain_at(loc,
1040  timeid, builder_terrain_type);
1041 
1043 
1044  const time_of_day& tod = get_time_of_day(loc);
1045 
1046  //get all the light transitions
1047  adjacent_loc_array_t adjs;
1048  std::array<const time_of_day*, 6> atods;
1049  get_adjacent_tiles(loc, adjs.data());
1050  for(size_t d = 0; d < adjs.size(); ++d){
1051  atods[d] = &get_time_of_day(adjs[d]);
1052  }
1053 
1054  for(int d=0; d<6; ++d){
1055  /* concave
1056  _____
1057  / \
1058  / atod1 \_____
1059  \ !tod / \
1060  \_____/ atod2 \
1061  / \__\ !tod /
1062  / \_____/
1063  \ tod /
1064  \_____/*/
1065 
1066  const time_of_day& atod1 = *atods[d];
1067  const time_of_day& atod2 = *atods[(d + 1) % 6];
1068 
1069  if(atod1.color == tod.color || atod2.color == tod.color || atod1.color != atod2.color)
1070  continue;
1071 
1072  if(lt.empty()) {
1073  //color the full hex before adding transitions
1074  tod_color col = tod.color + color_adjust_;
1075  lt = image::get_light_string(0, col.r, col.g, col.b);
1076  }
1077 
1078  // add the directional transitions
1079  tod_color acol = atod1.color + color_adjust_;
1080  lt += image::get_light_string(d + 1, acol.r, acol.g, acol.b);
1081  }
1082  for(int d=0; d<6; ++d){
1083  /* convex 1
1084  _____
1085  / \
1086  / atod1 \_____
1087  \ !tod / \
1088  \_____/ atod2 \
1089  / \__\ tod /
1090  / \_____/
1091  \ tod /
1092  \_____/*/
1093 
1094  const time_of_day& atod1 = *atods[d];
1095  const time_of_day& atod2 = *atods[(d + 1) % 6];
1096 
1097  if(atod1.color == tod.color || atod1.color == atod2.color)
1098  continue;
1099 
1100  if(lt.empty()) {
1101  //color the full hex before adding transitions
1102  tod_color col = tod.color + color_adjust_;
1103  lt = image::get_light_string(0, col.r, col.g, col.b);
1104  }
1105 
1106  // add the directional transitions
1107  tod_color acol = atod1.color + color_adjust_;
1108  lt += image::get_light_string(d + 7, acol.r, acol.g, acol.b);
1109  }
1110  for(int d=0; d<6; ++d){
1111  /* convex 2
1112  _____
1113  / \
1114  / atod1 \_____
1115  \ tod / \
1116  \_____/ atod2 \
1117  / \__\ !tod /
1118  / \_____/
1119  \ tod /
1120  \_____/*/
1121 
1122  const time_of_day& atod1 = *atods[d];
1123  const time_of_day& atod2 = *atods[(d + 1) % 6];
1124 
1125  if(atod2.color == tod.color || atod1.color == atod2.color)
1126  continue;
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 = atod2.color + color_adjust_;
1136  lt += image::get_light_string(d + 13, acol.r, acol.g, acol.b);
1137  }
1138 
1139  if(lt.empty()){
1140  tod_color col = tod.color + color_adjust_;
1141  if(!col.is_zero()){
1142  // no real lightmap needed but still color the hex
1143  lt = image::get_light_string(-1, col.r, col.g, col.b);
1144  }
1145  }
1146 
1147  if(terrains != nullptr) {
1148  // Cache the offmap name.
1149  // Since it is themeable it can change,
1150  // so don't make it static.
1151  const std::string off_map_name = "terrain/" + theme_.border().tile_image;
1152  for(const auto& terrain : *terrains) {
1153  const image::locator &image = animate_map_ ?
1154  terrain.get_current_frame() : terrain.get_first_frame();
1155 
1156  // We prevent ToD coloring and brightening of off-map tiles,
1157  // We need to test for the tile to be rendered and
1158  // not the location, since the transitions are rendered
1159  // over the offmap-terrain and these need a ToD coloring.
1160  surface surf;
1161  const bool off_map = (image.get_filename() == off_map_name || image.get_modifications().find("NO_TOD_SHIFT()") != std::string::npos);
1162 
1163  if(off_map) {
1164  surf = image::get_image(image, image::SCALED_TO_HEX);
1165  } else if(lt.empty()) {
1166  surf = image::get_image(image, image::SCALED_TO_HEX);
1167  } else {
1168  surf = image::get_lighted_image(image, lt, image::SCALED_TO_HEX);
1169  }
1170 
1171  if (!surf.null()) {
1172  terrain_image_vector_.push_back(std::move(surf));
1173  }
1174  }
1175  }
1176 }
1177 
1178 void display::drawing_buffer_add(const drawing_layer layer,
1179  const map_location& loc, int x, int y, const surface& surf,
1180  const SDL_Rect &clip)
1181 {
1182  drawing_buffer_.emplace_back(layer, loc, x, y, surf, clip);
1183 }
1184 
1185 void display::drawing_buffer_add(const drawing_layer layer,
1186  const map_location& loc, int x, int y,
1187  const std::vector<surface> &surf,
1188  const SDL_Rect &clip)
1189 {
1190  drawing_buffer_.emplace_back(layer, loc, x, y, surf, clip);
1191 }
1192 
1193 // FIXME: temporary method. Group splitting should be made
1194 // public into the definition of drawing_layer
1195 //
1196 // The drawing is done per layer_group, the range per group is [low, high).
1197 const std::array<display::drawing_layer, 4> display::drawing_buffer_key::layer_groups {{
1198  LAYER_TERRAIN_BG,
1199  LAYER_UNIT_FIRST,
1200  LAYER_UNIT_MOVE_DEFAULT,
1201  // Make sure the movement doesn't show above fog and reachmap.
1202  LAYER_REACHMAP
1203 }};
1204 
1205 enum {
1206  // you may adjust the following when needed:
1207 
1208  // maximum border. 3 should be safe even if a larger border is in use somewhere
1209  MAX_BORDER = 3,
1210 
1211  // store x, y, and layer in one 32 bit integer
1212  // 4 most significant bits == layer group => 16
1213  BITS_FOR_LAYER_GROUP = 4,
1214 
1215  // 10 second most significant bits == y => 1024
1216  BITS_FOR_Y = 10,
1217 
1218  // 1 third most significant bit == x parity => 2
1219  BITS_FOR_X_PARITY = 1,
1220 
1221  // 8 fourth most significant bits == layer => 256
1222  BITS_FOR_LAYER = 8,
1223 
1224  // 9 least significant bits == x / 2 => 512 (really 1024 for x)
1225  BITS_FOR_X_OVER_2 = 9
1226 };
1227 
1228 inline display::drawing_buffer_key::drawing_buffer_key(const map_location &loc, drawing_layer layer)
1229  : key_(0)
1230 {
1231  // Start with the index of last group entry...
1232  unsigned int group_i = layer_groups.size() - 1;
1233 
1234  // ...and works backwards until the group containing the specified layer is found.
1235  while(layer < layer_groups[group_i]) {
1236  --group_i;
1237  }
1238 
1239  enum {
1240  SHIFT_LAYER = BITS_FOR_X_OVER_2,
1241  SHIFT_X_PARITY = BITS_FOR_LAYER + SHIFT_LAYER,
1242  SHIFT_Y = BITS_FOR_X_PARITY + SHIFT_X_PARITY,
1243  SHIFT_LAYER_GROUP = BITS_FOR_Y + SHIFT_Y
1244  };
1245  static_assert(SHIFT_LAYER_GROUP + BITS_FOR_LAYER_GROUP == sizeof(key_) * 8, "Bit field too small");
1246 
1247  // the parity of x must be more significant than the layer but less significant than y.
1248  // Thus basically every row is split in two: First the row containing all the odd x
1249  // then the row containing all the even x. Since thus the least significant bit of x is
1250  // not required for x ordering anymore it can be shifted out to the right.
1251  const unsigned int x_parity = static_cast<unsigned int>(loc.x) & 1;
1252  key_ = (group_i << SHIFT_LAYER_GROUP) | (static_cast<unsigned int>(loc.y + MAX_BORDER) << SHIFT_Y);
1253  key_ |= (x_parity << SHIFT_X_PARITY);
1254  key_ |= (static_cast<unsigned int>(layer) << SHIFT_LAYER) | static_cast<unsigned int>(loc.x + MAX_BORDER) / 2;
1255 }
1256 
1257 void display::drawing_buffer_commit()
1258 {
1259  // std::list::sort() is a stable sort
1260  drawing_buffer_.sort();
1261 
1262  SDL_Rect clip_rect = map_area();
1263  surface& screen = get_screen_surface();
1264  clip_rect_setter set_clip_rect(screen, &clip_rect);
1265 
1266  /*
1267  * Info regarding the rendering algorithm.
1268  *
1269  * In order to render a hex properly it needs to be rendered per row. On
1270  * this row several layers need to be drawn at the same time. Mainly the
1271  * unit and the background terrain. This is needed since both can spill
1272  * in the next hex. The foreground terrain needs to be drawn before to
1273  * avoid decapitation a unit.
1274  *
1275  * This ended in the following priority order:
1276  * layergroup > location > layer > 'blit_helper' > surface
1277  */
1278 
1279  for (const blit_helper &blit : drawing_buffer_) {
1280  for (const surface& surf : blit.surf()) {
1281  // Note that dstrect can be changed by sdl_blit
1282  // and so a new instance should be initialized
1283  // to pass to each call to sdl_blit.
1284  SDL_Rect dstrect {blit.x(), blit.y(), 0, 0};
1285  SDL_Rect srcrect = blit.clip();
1286  SDL_Rect *srcrectArg = (srcrect.x | srcrect.y | srcrect.w | srcrect.h)
1287  ? &srcrect : nullptr;
1288  sdl_blit(surf, srcrectArg, screen, &dstrect);
1289  //NOTE: the screen part should already be marked as 'to update'
1290  }
1291  }
1293 }
1294 
1296 {
1297  drawing_buffer_.clear();
1298 }
1299 
1301 {
1302  benchmark = !benchmark;
1303 }
1304 
1306 {
1307  debug_foreground = !debug_foreground;
1308 }
1309 
1311 {
1312  if(video().faked()) {
1313  return;
1314  }
1315 
1316  surface& frameBuffer = video().getSurface();
1317 
1318  font::draw_floating_labels(frameBuffer);
1320 
1321  video().flip();
1322 
1324  font::undraw_floating_labels(frameBuffer);
1325 }
1326 
1327 // frametime is in milliseconds
1328 static unsigned calculate_fps(unsigned frametime)
1329 {
1330  return frametime != 0u ? 1000u / frametime : 999u;
1331 }
1332 
1334 {
1335  if (screen_.update_locked()) {
1336  return;
1337  }
1338 
1339  if(preferences::show_fps() || benchmark) {
1340  static int frames = 0;
1341  ++frames;
1342  const int sample_freq = 10;
1343  if(frames == sample_freq) {
1344  const auto minmax_it = std::minmax_element(frametimes_.begin(), frametimes_.end());
1345  const unsigned render_avg = std::accumulate(frametimes_.begin(), frametimes_.end(), 0) / frametimes_.size();
1346  const int avg_fps = calculate_fps(render_avg);
1347  const int max_fps = calculate_fps(*minmax_it.first);
1348  const int min_fps = calculate_fps(*minmax_it.second);
1349  frames = 0;
1350 
1351  if(fps_handle_ != 0) {
1353  fps_handle_ = 0;
1354  }
1355  std::ostringstream stream;
1356  stream << "<tt> min/avg/max/act</tt>\n";
1357  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";
1358  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";
1359  if (game_config::debug) {
1360  stream << "\nhex: " << drawn_hexes_*1.0/sample_freq;
1362  stream << " (" << (invalidated_hexes_-drawn_hexes_)*1.0/sample_freq << ")";
1363  }
1364  drawn_hexes_ = 0;
1365  invalidated_hexes_ = 0;
1366 
1367  font::floating_label flabel(stream.str());
1368  flabel.set_font_size(12);
1369  flabel.set_color(benchmark ? font::BAD_COLOR : font::NORMAL_COLOR);
1370  flabel.set_position(10, 100);
1371  flabel.set_alignment(font::LEFT_ALIGN);
1372 
1374  }
1375  } else if(fps_handle_ != 0) {
1377  fps_handle_ = 0;
1378  drawn_hexes_ = 0;
1379  invalidated_hexes_ = 0;
1380  }
1381 
1382  flip();
1383 }
1384 static void draw_panel(CVideo &video, const theme::panel& panel, std::vector<std::shared_ptr<gui::button>>& /*buttons*/)
1385 {
1386  //log_scope("draw panel");
1387  DBG_DP << "drawing panel " << panel.get_id() << "\n";
1388 
1389  surface surf(image::get_image(panel.image()));
1390 
1391  const SDL_Rect screen = video.screen_area();
1392  SDL_Rect& loc = panel.location(screen);
1393 
1394  DBG_DP << "panel location: x=" << loc.x << ", y=" << loc.y
1395  << ", w=" << loc.w << ", h=" << loc.h << "\n";
1396 
1397  if(!surf.null()) {
1398  if(surf->w != loc.w || surf->h != loc.h) {
1399  surf.assign(tile_surface(surf,loc.w,loc.h));
1400  }
1401  video.blit_surface(loc.x, loc.y, surf);
1402  }
1403 }
1404 
1405 static void draw_label(CVideo& video, surface target, const theme::label& label)
1406 {
1407  //log_scope("draw label");
1408 
1409  const color_t& RGB = label.font_rgb();
1410 
1411  std::string c_start="<";
1412  std::string c_sep=",";
1413  std::string c_end=">";
1414  std::stringstream color;
1415  color<< c_start << RGB.r << c_sep << RGB.g << c_sep << RGB.b << c_end;
1416  std::string text = label.text();
1417 
1418  if(label.font_rgb_set()) {
1419  color<<text;
1420  text = color.str();
1421  }
1422  const std::string& icon = label.icon();
1423  SDL_Rect& loc = label.location(video.screen_area());
1424 
1425  if(icon.empty() == false) {
1426  surface surf(image::get_image(icon));
1427  if(!surf.null()) {
1428  if(surf->w > loc.w || surf->h > loc.h) {
1429  surf.assign(scale_surface(surf,loc.w,loc.h));
1430  }
1431 
1432  sdl_blit(surf,nullptr,target,&loc);
1433  }
1434 
1435  if(text.empty() == false) {
1436  tooltips::add_tooltip(loc,text);
1437  }
1438  } else if(text.empty() == false) {
1439  font::draw_text(&video,loc,label.font_size(),font::NORMAL_COLOR,text,loc.x,loc.y);
1440  }
1441 
1442 }
1443 
1445 {
1446  surface& screen(screen_.getSurface());
1447 
1448  /*
1449  * The minimap is also a panel, force it to update its contents.
1450  * This is required when the size of the minimap has been modified.
1451  */
1453 
1454  for(const auto& panel : theme_.panels()) {
1455  draw_panel(video(), panel, menu_buttons_);
1456  }
1457 
1458  for(const auto& label : theme_.labels()) {
1459  draw_label(video(), screen, label);
1460  }
1461 
1462  render_buttons();
1463 }
1464 
1465 static void draw_background(surface screen, const SDL_Rect& area, const std::string& image)
1466 {
1467  // No background image, just fill in black.
1468  if(image.empty()) {
1469  sdl::fill_rectangle(area, color_t(0, 0, 0));
1470  return;
1471  }
1472 
1473  const surface background(image::get_image(image));
1474  if(background.null()) {
1475  return;
1476  }
1477 
1478  const unsigned int width = background->w;
1479  const unsigned int height = background->h;
1480 
1481  const unsigned int w_count = static_cast<int>(std::ceil(static_cast<double>(area.w) / static_cast<double>(width)));
1482  const unsigned int h_count = static_cast<int>(std::ceil(static_cast<double>(area.h) / static_cast<double>(height)));
1483 
1484  for(unsigned int w = 0, w_off = area.x; w < w_count; ++w, w_off += width) {
1485  for(unsigned int h = 0, h_off = area.y; h < h_count; ++h, h_off += height) {
1486  SDL_Rect clip = sdl::create_rect(w_off, h_off, 0, 0);
1487  sdl_blit(background, nullptr, screen, &clip);
1488  }
1489  }
1490 }
1491 
1493  const drawing_layer layer,
1494  const std::string& text,
1495  std::size_t font_size,
1496  color_t color,
1497  double x_in_hex,
1498  double y_in_hex)
1499 {
1500  if (text.empty()) return;
1501 
1502  const std::size_t font_sz = static_cast<std::size_t>(font_size * get_zoom_factor());
1503 
1504  surface text_surf = font::get_rendered_text(text, font_sz, color);
1505  surface back_surf = font::get_rendered_text(text, font_sz, font::BLACK_COLOR);
1506  const int x = get_location_x(loc) - text_surf->w/2
1507  + static_cast<int>(x_in_hex* hex_size());
1508  const int y = get_location_y(loc) - text_surf->h/2
1509  + static_cast<int>(y_in_hex* hex_size());
1510  for (int dy=-1; dy <= 1; ++dy) {
1511  for (int dx=-1; dx <= 1; ++dx) {
1512  if (dx!=0 || dy!=0) {
1513  drawing_buffer_add(layer, loc, x + dx, y + dy, back_surf);
1514  }
1515  }
1516  }
1517  drawing_buffer_add(layer, loc, x, y, text_surf);
1518 }
1519 
1520 //TODO: convert this to use sdl::ttexture
1522  const map_location& loc, surface image,
1523  bool hreverse, bool greyscale, fixed_t alpha,
1524  color_t blendto, double blend_ratio, double submerged, bool vreverse)
1525 {
1526  if (image==nullptr)
1527  return;
1528 
1529  SDL_Rect image_rect {x, y, image->w, image->h};
1530  SDL_Rect clip_rect = map_area();
1531  if (!sdl::rects_overlap(image_rect, clip_rect))
1532  return;
1533 
1534  surface surf(image);
1535 
1536  if(hreverse) {
1537  surf = image::reverse_image(surf);
1538  }
1539  if(vreverse) {
1540  surf = flop_surface(surf);
1541  }
1542 
1543  if(greyscale) {
1544  surf = greyscale_image(surf);
1545  }
1546 
1547  if(blend_ratio != 0) {
1548  surf = blend_surface(surf, blend_ratio, blendto);
1549  }
1550  if(alpha > ftofxp(1.0)) {
1551  surf = brighten_image(surf, alpha);
1552  //} else if(alpha != 1.0 && blendto != 0) {
1553  // surf.assign(blend_surface(surf,1.0-alpha,blendto));
1554  } else if(alpha != ftofxp(1.0)) {
1555  surface temp = make_neutral_surface(surf);
1556  adjust_surface_alpha(temp, alpha);
1557  surf = temp;
1558  }
1559 
1560  if(surf == nullptr) {
1561  ERR_DP << "surface lost..." << std::endl;
1562  return;
1563  }
1564 
1565  if(submerged > 0.0) {
1566  // divide the surface into 2 parts
1567  const int submerge_height = std::max<int>(0, surf->h*(1.0-submerged));
1568  const int depth = surf->h - submerge_height;
1569  SDL_Rect srcrect {0, 0, surf->w, submerge_height};
1570  drawing_buffer_add(drawing_layer, loc, x, y, surf, srcrect);
1571 
1572  if(submerge_height != surf->h) {
1573  //the lower part will be transparent
1574  float alpha_base = 0.3f; // 30% alpha at surface of water
1575  float alpha_delta = 0.015f; // lose 1.5% per pixel depth
1576  alpha_delta *= zoom_ / DefaultZoom; // adjust with zoom
1577  surf = submerge_alpha(surf, depth, alpha_base, alpha_delta);
1578 
1579  srcrect.y = submerge_height;
1580  srcrect.h = surf->h-submerge_height;
1581  y += submerge_height;
1582 
1583  drawing_buffer_add(drawing_layer, loc, x, y, surf, srcrect);
1584  }
1585  } else {
1586  // simple blit
1587  drawing_buffer_add(drawing_layer, loc, x, y, surf);
1588  }
1589 }
1591 {
1593  selectedHex_ = hex;
1596 }
1597 
1599 {
1601  mouseoverHex_ = hex;
1603 }
1604 
1605 void display::set_diagnostic(const std::string& msg)
1606 {
1607  if(diagnostic_label_ != 0) {
1609  diagnostic_label_ = 0;
1610  }
1611 
1612  if(!msg.empty()) {
1613  font::floating_label flabel(msg);
1615  flabel.set_color(font::YELLOW_COLOR);
1616  flabel.set_position(300, 50);
1617  flabel.set_clip_rect(map_outside_area());
1618 
1620  }
1621 }
1622 
1624 {
1625  if (get_map().empty()) {
1626  return;
1627  }
1628 
1629  if(benchmark) {
1630  invalidateAll_ = true;
1631  }
1632 
1633  if(!panelsDrawn_) {
1634  draw_all_panels();
1635  panelsDrawn_ = true;
1636  }
1637 
1638  if(redraw_background_) {
1639  // Full redraw of the background
1640  const SDL_Rect clip_rect = map_outside_area();
1641  const surface& screen = get_screen_surface();
1642  clip_rect_setter set_clip_rect(screen, &clip_rect);
1643  SDL_FillRect(screen, &clip_rect, 0x00000000);
1644  draw_background(screen, clip_rect, theme_.border().background_image);
1645  redraw_background_ = false;
1646 
1647  // Force a full map redraw
1648  invalidateAll_ = true;
1649  }
1650 
1651  if(invalidateAll_) {
1652  DBG_DP << "draw() with invalidateAll\n";
1653 
1654  // toggle invalidateAll_ first to allow regular invalidations
1655  invalidateAll_ = false;
1657 
1658  redrawMinimap_ = true;
1659  }
1660 }
1661 
1662 void display::draw_wrap(bool update, bool force)
1663 {
1664  static int time_between_draws = preferences::draw_delay();
1665  if(time_between_draws < 0) {
1666  time_between_draws = 1000 / screen_.current_refresh_rate();
1667  }
1668 
1669  if(redrawMinimap_) {
1670  redrawMinimap_ = false;
1671  draw_minimap();
1672  }
1673 
1674  if(update) {
1675  update_display();
1676 
1677  frametimes_.push_back(SDL_GetTicks() - last_frame_finished_);
1678  fps_counter_++;
1679  using std::chrono::duration_cast;
1680  using std::chrono::seconds;
1681  using std::chrono::steady_clock;
1682  const seconds current_second = duration_cast<seconds>(steady_clock::now().time_since_epoch());
1683  if(current_second != fps_start_) {
1684  fps_start_ = current_second;
1686  fps_counter_ = 0;
1687  }
1688  int longest_frame = *std::max_element(frametimes_.begin(), frametimes_.end());
1689  int wait_time = time_between_draws - longest_frame;
1690 
1691  if(!force && !benchmark && wait_time > 0) {
1692  // If it's not time yet to draw, delay until it is
1693  SDL_Delay(wait_time);
1694  }
1695 
1696  last_frame_finished_ = SDL_GetTicks();
1697  }
1698 }
1699 
1701 {
1702  for(auto i = action_buttons_.begin(); i != action_buttons_.end(); ++i) {
1703  if((*i)->pressed()) {
1704  const std::size_t index = std::distance(action_buttons_.begin(), i);
1705  if(index >= theme_.actions().size()) {
1706  assert(false);
1707  return nullptr;
1708  }
1709  return &theme_.actions()[index];
1710  }
1711  }
1712 
1713  return nullptr;
1714 }
1715 
1717 {
1718  for(auto i = menu_buttons_.begin(); i != menu_buttons_.end(); ++i) {
1719  if((*i)->pressed()) {
1720  const std::size_t index = std::distance(menu_buttons_.begin(), i);
1721  if(index >= theme_.menus().size()) {
1722  assert(false);
1723  return nullptr;
1724  }
1725  return theme_.get_menu_item((*i)->id());
1726  }
1727  }
1728 
1729  return nullptr;
1730 }
1731 
1732 void display::enable_menu(const std::string& item, bool enable)
1733 {
1734  for(auto menu = theme_.menus().begin(); menu != theme_.menus().end(); ++menu) {
1735 
1736  const auto hasitem = std::find_if(menu->items().begin(), menu->items().end(),
1737  [&item](const config& c) { return c["id"].str() == item; }
1738  );
1739 
1740  if(hasitem != menu->items().end()) {
1741  const std::size_t index = std::distance(theme_.menus().begin(), menu);
1742  if(index >= menu_buttons_.size()) {
1743  continue;
1744  }
1745  menu_buttons_[index]->enable(enable);
1746  }
1747  }
1748 }
1749 
1750 void display::announce(const std::string& message, const color_t& color, const announce_options& options)
1751 {
1752  if(options.discard_previous) {
1753  font::remove_floating_label(prevLabel);
1754  }
1755  font::floating_label flabel(message);
1757  flabel.set_color(color);
1759  map_outside_area().y + map_outside_area().h/3);
1760  flabel.set_lifetime(options.lifetime);
1761  flabel.set_clip_rect(map_outside_area());
1762 
1763  prevLabel = font::add_floating_label(flabel);
1764 }
1765 
1767 {
1768  const SDL_Rect& area = minimap_area();
1769 
1770  if(area.w == 0 || area.h == 0) {
1771  return;
1772  }
1773 
1774  if(minimap_ == nullptr || minimap_->w > area.w || minimap_->h > area.h) {
1775  minimap_ = image::getMinimap(area.w, area.h, get_map(),
1776  dc_->teams().empty() ? nullptr : &dc_->teams()[currentTeam_],
1777  (selectedHex_.valid() && !is_blindfolded()) ? &reach_map_ : nullptr);
1778  if(minimap_ == nullptr) {
1779  return;
1780  }
1781  }
1782 
1783  const surface& screen(screen_.getSurface());
1784  clip_rect_setter clip_setter(screen, &area);
1785 
1786  color_t back_color {31,31,23,SDL_ALPHA_OPAQUE};
1787  draw_centered_on_background(minimap_, area, back_color, screen);
1788 
1789  //update the minimap location for mouse and units functions
1790  minimap_location_.x = area.x + (area.w - minimap_->w) / 2;
1791  minimap_location_.y = area.y + (area.h - minimap_->h) / 2;
1792  minimap_location_.w = minimap_->w;
1793  minimap_location_.h = minimap_->h;
1794 
1796 
1797  // calculate the visible portion of the map:
1798  // scaling between minimap and full map images
1799  double xscaling = 1.0*minimap_->w / (get_map().w()*hex_width());
1800  double yscaling = 1.0*minimap_->h / (get_map().h()*hex_size());
1801 
1802  // we need to shift with the border size
1803  // and the 0.25 from the minimap balanced drawing
1804  // and the possible difference between real map and outside off-map
1805  SDL_Rect map_rect = map_area();
1806  SDL_Rect map_out_rect = map_outside_area();
1807  double border = theme_.border().size;
1808  double shift_x = - border*hex_width() - (map_out_rect.w - map_rect.w) / 2;
1809  double shift_y = - (border+0.25)*hex_size() - (map_out_rect.h - map_rect.h) / 2;
1810 
1811  int view_x = static_cast<int>((xpos_ + shift_x) * xscaling);
1812  int view_y = static_cast<int>((ypos_ + shift_y) * yscaling);
1813  int view_w = static_cast<int>(map_out_rect.w * xscaling);
1814  int view_h = static_cast<int>(map_out_rect.h * yscaling);
1815 
1816  SDL_Rect outline_rect {
1817  minimap_location_.x + view_x - 1,
1818  minimap_location_.y + view_y - 1,
1819  view_w + 2,
1820  view_h + 2
1821  };
1822 
1823  sdl::draw_rectangle(outline_rect, {255, 255, 255, 255});
1824 }
1825 
1827 {
1828  if (!preferences::minimap_draw_units() || is_blindfolded()) return;
1829 
1830  double xscaling = 1.0 * minimap_location_.w / get_map().w();
1831  double yscaling = 1.0 * minimap_location_.h / get_map().h();
1832 
1833  for(const auto& u : dc_->units()) {
1834  if (fogged(u.get_location()) ||
1835  (dc_->teams()[currentTeam_].is_enemy(u.side()) &&
1836  u.invisible(u.get_location())) ||
1837  u.get_hidden()) {
1838  continue;
1839  }
1840 
1841  int side = u.side();
1842  color_t col = team::get_minimap_color(side);
1843 
1845 
1846  if (dc_->teams()[currentTeam_].is_enemy(side)) {
1848  } else {
1849 
1850  if (currentTeam_ +1 == static_cast<unsigned>(side)) {
1851 
1852  if (u.movement_left() == u.total_movement())
1854  else if (u.movement_left() == 0)
1856  else
1858 
1859  } else
1861  }
1862  }
1863 
1864  double u_x = u.get_location().x * xscaling;
1865  double u_y = (u.get_location().y + (is_odd(u.get_location().x) ? 1 : -1)/4.0) * yscaling;
1866  // use 4/3 to compensate the horizontal hexes imbrication
1867  double u_w = 4.0 / 3.0 * xscaling;
1868  double u_h = yscaling;
1869 
1870  SDL_Rect r {
1871  minimap_location_.x + int(std::round(u_x))
1872  , minimap_location_.y + int(std::round(u_y))
1873  , int(std::round(u_w))
1874  , int(std::round(u_h))
1875  };
1876 
1877  sdl::fill_rectangle(r, col);
1878  }
1879 }
1880 
1881 bool display::scroll(int xmove, int ymove, bool force)
1882 {
1883  if(view_locked_ && !force) {
1884  return false;
1885  }
1886 
1887  // No move offset, do nothing.
1888  if(xmove == 0 && ymove == 0) {
1889  return false;
1890  }
1891 
1892  int new_x = xpos_ + xmove;
1893  int new_y = ypos_ + ymove;
1894 
1895  bounds_check_position(new_x, new_y);
1896 
1897  // Camera position doesn't change, exit.
1898  if(xpos_ == new_x && ypos_ == new_y) {
1899  return false;
1900  }
1901 
1902  const int diff_x = xpos_ - new_x;
1903  const int diff_y = ypos_ - new_y;
1904 
1905  xpos_ = new_x;
1906  ypos_ = new_y;
1907 
1908  /* Adjust floating label positions. This only affects labels whose position is anchored
1909  * to the map instead of the screen. In order to do that, we want to adjust their drawing
1910  * coordinates in the opposite direction of the screen scroll.
1911  *
1912  * The check a few lines up prevents any scrolling from happening if the camera position
1913  * doesn't change. Without that, the label still scroll even when the map edge is reached.
1914  * If that's removed, the following formula should work instead:
1915  *
1916  * const int label_[x,y]_adjust = [x,y]pos_ - new_[x,y];
1917  */
1918  font::scroll_floating_labels(diff_x, diff_y);
1919 
1921 
1922  //
1923  // NOTE: the next three blocks can be removed once we switch to accelerated rendering.
1924  //
1925 
1926  if(!screen_.update_locked()) {
1927  surface& screen(screen_.getSurface());
1928 
1929  SDL_Rect dstrect = map_area();
1930  dstrect.x += diff_x;
1931  dstrect.y += diff_y;
1932  dstrect = sdl::intersect_rects(dstrect, map_area());
1933 
1934  SDL_Rect srcrect = dstrect;
1935  srcrect.x -= diff_x;
1936  srcrect.y -= diff_y;
1937 
1938  // This is a workaround for a SDL2 bug when blitting on overlapping surfaces. The bug
1939  // only strikes during scrolling, but will then duplicate textures across the entire map.
1940  surface screen_copy = make_neutral_surface(screen);
1941 
1942  SDL_SetSurfaceBlendMode(screen_copy, SDL_BLENDMODE_NONE);
1943  SDL_BlitSurface(screen_copy, &srcrect, screen, &dstrect);
1944  }
1945 
1946  if(diff_y != 0) {
1947  SDL_Rect r = map_area();
1948 
1949  if(diff_y < 0) {
1950  r.y = r.y + r.h + diff_y;
1951  }
1952 
1953  r.h = std::abs(diff_y);
1955  }
1956 
1957  if(diff_x != 0) {
1958  SDL_Rect r = map_area();
1959 
1960  if(diff_x < 0) {
1961  r.x = r.x + r.w + diff_x;
1962  }
1963 
1964  r.w = std::abs(diff_x);
1966  }
1967 
1969 
1970  redrawMinimap_ = true;
1971 
1972  return true;
1973 }
1974 
1976 {
1977  return zoom_ == MaxZoom;
1978 }
1979 
1981 {
1982  return zoom_ == MinZoom;
1983 }
1984 
1985 bool display::set_zoom(bool increase)
1986 {
1987  // Ensure we don't try to access nonexistent vector indices.
1988  zoom_index_ = utils::clamp(increase ? zoom_index_ + 1 : zoom_index_ - 1, 0, final_zoom_index);
1989 
1990  // No validation check is needed in the next step since we've already set the index here and
1991  // know the new zoom value is indeed valid.
1992  return set_zoom(zoom_levels[zoom_index_], false);
1993 }
1994 
1995 bool display::set_zoom(unsigned int amount, const bool validate_value_and_set_index)
1996 {
1997  unsigned int new_zoom = utils::clamp(amount, MinZoom, MaxZoom);
1998 
1999  LOG_DP << "new_zoom = " << new_zoom << std::endl;
2000 
2001  if(new_zoom == zoom_) {
2002  return false;
2003  }
2004 
2005  // Confirm this is indeed a valid zoom level.
2006  if(validate_value_and_set_index) {
2007  auto iter = std::lower_bound(zoom_levels.begin(), zoom_levels.end(), new_zoom);
2008 
2009  if(iter == zoom_levels.end()) {
2010  // This should never happen, since the value was already clamped earlier
2011  return false;
2012  } else if(iter != zoom_levels.begin()) {
2013  float diff = *iter - *(iter - 1);
2014  float lower = (new_zoom - *(iter - 1)) / diff;
2015  float upper = (*iter - new_zoom) / diff;
2016  if(lower < upper) {
2017  // It's actually closer to the previous element.
2018  iter--;
2019  }
2020  }
2021 
2022  new_zoom = *iter;
2023  zoom_index_ = std::distance(zoom_levels.begin(), iter);
2024  }
2025 
2026  const SDL_Rect& area = map_area();
2027 
2028  // Turn the zoom factor to a double in order to avoid rounding errors.
2029  double zoom_factor = static_cast<double>(new_zoom) / static_cast<double>(zoom_);
2030 
2031  xpos_ = std::round(((xpos_ + area.w / 2) * zoom_factor) - (area.w / 2));
2032  ypos_ = std::round(((ypos_ + area.h / 2) * zoom_factor) - (area.h / 2));
2033 
2034  zoom_ = new_zoom;
2036  if(zoom_ != DefaultZoom) {
2037  last_zoom_ = zoom_;
2038  }
2039 
2041 
2043  redraw_background_ = true;
2044  invalidate_all();
2045 
2046  // Forces a redraw after zooming.
2047  // This prevents some graphic glitches from occurring.
2048  draw();
2049 
2050  return true;
2051 }
2052 
2054 {
2055  if (zoom_ != DefaultZoom) {
2056  last_zoom_ = zoom_;
2058  } else {
2059  // When we are already at the default zoom,
2060  // switch to the last zoom used
2062  }
2063 }
2064 
2066 {
2067  int x = get_location_x(loc);
2068  int y = get_location_y(loc);
2069  return !outside_area(map_area(), x, y);
2070 }
2071 
2073 {
2074  int x = get_location_x(loc);
2075  int y = get_location_y(loc);
2076  const SDL_Rect &area = map_area();
2077  int hw = hex_width(), hs = hex_size();
2078  return x + hs >= area.x - hw && x < area.x + area.w + hw &&
2079  y + hs >= area.y - hs && y < area.y + area.h + hs;
2080 }
2081 
2082 void display::scroll_to_xy(int screenxpos, int screenypos, SCROLL_TYPE scroll_type, bool force)
2083 {
2084  if(!force && (view_locked_ || !preferences::scroll_to_action())) return;
2085  if(screen_.update_locked()) {
2086  return;
2087  }
2088  const SDL_Rect area = map_area();
2089  const int xmove_expected = screenxpos - (area.x + area.w/2);
2090  const int ymove_expected = screenypos - (area.y + area.h/2);
2091 
2092  int xpos = xpos_ + xmove_expected;
2093  int ypos = ypos_ + ymove_expected;
2094  bounds_check_position(xpos, ypos);
2095  int xmove = xpos - xpos_;
2096  int ymove = ypos - ypos_;
2097 
2098  if(scroll_type == WARP || scroll_type == ONSCREEN_WARP || turbo_speed() > 2.0 || preferences::scroll_speed() > 99) {
2099  scroll(xmove,ymove,true);
2100  draw();
2101  return;
2102  }
2103 
2104  // Doing an animated scroll, with acceleration etc.
2105 
2106  int x_old = 0;
2107  int y_old = 0;
2108 
2109  const double dist_total = std::hypot(xmove, ymove);
2110  double dist_moved = 0.0;
2111 
2112  int t_prev = SDL_GetTicks();
2113 
2114  double velocity = 0.0;
2115  while (dist_moved < dist_total) {
2116  events::pump();
2117 
2118  int t = SDL_GetTicks();
2119  double dt = (t - t_prev) / 1000.0;
2120  if (dt > 0.200) {
2121  // Do not skip too many frames on slow PCs
2122  dt = 0.200;
2123  }
2124  t_prev = t;
2125 
2126  const double accel_time = 0.3 / turbo_speed(); // seconds until full speed is reached
2127  const double decel_time = 0.4 / turbo_speed(); // seconds from full speed to stop
2128 
2129  double velocity_max = preferences::scroll_speed() * 60.0;
2130  velocity_max *= turbo_speed();
2131  double accel = velocity_max / accel_time;
2132  double decel = velocity_max / decel_time;
2133 
2134  // If we started to decelerate now, where would we stop?
2135  double stop_time = velocity / decel;
2136  double dist_stop = dist_moved + velocity*stop_time - 0.5*decel*stop_time*stop_time;
2137  if (dist_stop > dist_total || velocity > velocity_max) {
2138  velocity -= decel * dt;
2139  if (velocity < 1.0) velocity = 1.0;
2140  } else {
2141  velocity += accel * dt;
2142  if (velocity > velocity_max) velocity = velocity_max;
2143  }
2144 
2145  dist_moved += velocity * dt;
2146  if (dist_moved > dist_total) dist_moved = dist_total;
2147 
2148  int x_new = std::round(xmove * dist_moved / dist_total);
2149  int y_new = std::round(ymove * dist_moved / dist_total);
2150 
2151  int dx = x_new - x_old;
2152  int dy = y_new - y_old;
2153 
2154  scroll(dx,dy,true);
2155  x_old += dx;
2156  y_old += dy;
2157  draw();
2158  }
2159 }
2160 
2161 void display::scroll_to_tile(const map_location& loc, SCROLL_TYPE scroll_type, bool check_fogged, bool force)
2162 {
2163  if(get_map().on_board(loc) == false) {
2164  ERR_DP << "Tile at " << loc << " isn't on the map, can't scroll to the tile." << std::endl;
2165  return;
2166  }
2167 
2168  std::vector<map_location> locs;
2169  locs.push_back(loc);
2170  scroll_to_tiles(locs, scroll_type, check_fogged,false,0.0,force);
2171 }
2172 
2174  SCROLL_TYPE scroll_type, bool check_fogged,
2175  double add_spacing, bool force)
2176 {
2177  std::vector<map_location> locs;
2178  locs.push_back(loc1);
2179  locs.push_back(loc2);
2180  scroll_to_tiles(locs, scroll_type, check_fogged, false, add_spacing,force);
2181 }
2182 
2183 void display::scroll_to_tiles(const std::vector<map_location>::const_iterator & begin,
2184  const std::vector<map_location>::const_iterator & end,
2185  SCROLL_TYPE scroll_type, bool check_fogged,
2186  bool only_if_possible, double add_spacing, bool force)
2187 {
2188  // basically we calculate the min/max coordinates we want to have on-screen
2189  int minx = 0;
2190  int maxx = 0;
2191  int miny = 0;
2192  int maxy = 0;
2193  bool valid = false;
2194 
2195  for(std::vector<map_location>::const_iterator itor = begin; itor != end ; ++itor) {
2196  if(get_map().on_board(*itor) == false) continue;
2197  if(check_fogged && fogged(*itor)) continue;
2198 
2199  int x = get_location_x(*itor);
2200  int y = get_location_y(*itor);
2201 
2202  if (!valid) {
2203  minx = x;
2204  maxx = x;
2205  miny = y;
2206  maxy = y;
2207  valid = true;
2208  } else {
2209  int minx_new = std::min<int>(minx,x);
2210  int miny_new = std::min<int>(miny,y);
2211  int maxx_new = std::max<int>(maxx,x);
2212  int maxy_new = std::max<int>(maxy,y);
2213  SDL_Rect r = map_area();
2214  r.x = minx_new;
2215  r.y = miny_new;
2216  if(outside_area(r, maxx_new, maxy_new)) {
2217  // we cannot fit all locations to the screen
2218  if (only_if_possible) return;
2219  break;
2220  }
2221  minx = minx_new;
2222  miny = miny_new;
2223  maxx = maxx_new;
2224  maxy = maxy_new;
2225  }
2226  }
2227  //if everything is fogged or the location list is empty
2228  if(!valid) return;
2229 
2230  if (scroll_type == ONSCREEN || scroll_type == ONSCREEN_WARP) {
2231  SDL_Rect r = map_area();
2232  int spacing = std::round(add_spacing * hex_size());
2233  r.x += spacing;
2234  r.y += spacing;
2235  r.w -= 2*spacing;
2236  r.h -= 2*spacing;
2237  if (!outside_area(r, minx,miny) && !outside_area(r, maxx,maxy)) {
2238  return;
2239  }
2240  }
2241 
2242  // let's do "normal" rectangle math from now on
2243  SDL_Rect locs_bbox;
2244  locs_bbox.x = minx;
2245  locs_bbox.y = miny;
2246  locs_bbox.w = maxx - minx + hex_size();
2247  locs_bbox.h = maxy - miny + hex_size();
2248 
2249  // target the center
2250  int target_x = locs_bbox.x + locs_bbox.w/2;
2251  int target_y = locs_bbox.y + locs_bbox.h/2;
2252 
2253  if (scroll_type == ONSCREEN || scroll_type == ONSCREEN_WARP) {
2254  // when doing an ONSCREEN scroll we do not center the target unless needed
2255  SDL_Rect r = map_area();
2256  int map_center_x = r.x + r.w/2;
2257  int map_center_y = r.y + r.h/2;
2258 
2259  int h = r.h;
2260  int w = r.w;
2261 
2262  // we do not want to be only inside the screen rect, but center a bit more
2263  double inside_frac = 0.5; // 0.0 = always center the target, 1.0 = scroll the minimum distance
2264  w = static_cast<int>(w * inside_frac);
2265  h = static_cast<int>(h * inside_frac);
2266 
2267  // shrink the rectangle by the size of the locations rectangle we found
2268  // such that the new task to fit a point into a rectangle instead of rectangle into rectangle
2269  w -= locs_bbox.w;
2270  h -= locs_bbox.h;
2271 
2272  if (w < 1) w = 1;
2273  if (h < 1) h = 1;
2274 
2275  r.x = target_x - w/2;
2276  r.y = target_y - h/2;
2277  r.w = w;
2278  r.h = h;
2279 
2280  // now any point within r is a possible target to scroll to
2281  // we take the one with the minimum distance to map_center
2282  // which will always be at the border of r
2283 
2284  if (map_center_x < r.x) {
2285  target_x = r.x;
2286  target_y = map_center_y;
2287  if (target_y < r.y) target_y = r.y;
2288  if (target_y > r.y+r.h-1) target_y = r.y+r.h-1;
2289  } else if (map_center_x > r.x+r.w-1) {
2290  target_x = r.x+r.w-1;
2291  target_y = map_center_y;
2292  if (target_y < r.y) target_y = r.y;
2293  if (target_y >= r.y+r.h) target_y = r.y+r.h-1;
2294  } else if (map_center_y < r.y) {
2295  target_y = r.y;
2296  target_x = map_center_x;
2297  if (target_x < r.x) target_x = r.x;
2298  if (target_x > r.x+r.w-1) target_x = r.x+r.w-1;
2299  } else if (map_center_y > r.y+r.h-1) {
2300  target_y = r.y+r.h-1;
2301  target_x = map_center_x;
2302  if (target_x < r.x) target_x = r.x;
2303  if (target_x > r.x+r.w-1) target_x = r.x+r.w-1;
2304  } else {
2305  ERR_DP << "Bug in the scrolling code? Looks like we would not need to scroll after all..." << std::endl;
2306  // keep the target at the center
2307  }
2308  }
2309 
2310  scroll_to_xy(target_x, target_y,scroll_type,force);
2311 }
2312 
2313 
2315 {
2316  const unsigned int orig_zoom = zoom_;
2317 
2318  if(zoom_ < MinZoom) {
2319  zoom_ = MinZoom;
2320  }
2321 
2322  if(zoom_ > MaxZoom) {
2323  zoom_ = MaxZoom;
2324  }
2325 
2327 
2328  if(zoom_ != orig_zoom) {
2330  }
2331 }
2332 
2333 void display::bounds_check_position(int& xpos, int& ypos) const
2334 {
2335  const int tile_width = hex_width();
2336 
2337  // Adjust for the border 2 times
2338  const int xend = static_cast<int>(tile_width * (get_map().w() + 2 * theme_.border().size) + tile_width/3);
2339  const int yend = static_cast<int>(zoom_ * (get_map().h() + 2 * theme_.border().size) + zoom_/2);
2340 
2341  if(xpos > xend - map_area().w) {
2342  xpos = xend - map_area().w;
2343  }
2344 
2345  if(ypos > yend - map_area().h) {
2346  ypos = yend - map_area().h;
2347  }
2348 
2349  if(xpos < 0) {
2350  xpos = 0;
2351  }
2352 
2353  if(ypos < 0) {
2354  ypos = 0;
2355  }
2356 }
2357 
2358 double display::turbo_speed() const
2359 {
2360  bool res = turbo_;
2361  if(keys_[SDLK_LSHIFT] || keys_[SDLK_RSHIFT]) {
2362  res = !res;
2363  }
2364 
2365  res |= screen_.faked();
2366  if (res)
2367  return turbo_speed_;
2368  else
2369  return 1.0;
2370 }
2371 
2373 {
2374  idle_anim_rate_ = std::pow(2.0, -rate/10.0);
2375 }
2376 
2378 {
2379  if(screen_.update_locked())
2380  return;
2381 
2382  invalidateGameStatus_ = true;
2383 
2384  reportRects_.clear();
2385  reportSurfaces_.clear();
2386  reports_.clear();
2387 
2389 
2391 
2393 
2394  if(!menu_buttons_.empty() || !action_buttons_.empty()) {
2395  create_buttons();
2396  }
2397 
2398  if(resources::controller) {
2400  if(command_executor != nullptr) {
2401  // This function adds button overlays,
2402  // it needs to be run after recreating the buttons.
2403  command_executor->set_button_state();
2404  }
2405  }
2406 
2407  panelsDrawn_ = false;
2408  if (!gui::in_dialog()) {
2410  }
2411 
2412  redraw_background_ = true;
2413 
2414  for(std::function<void(display&)> f : redraw_observers_) {
2415  f(*this);
2416  }
2417 
2418  int ticks1 = SDL_GetTicks();
2419  invalidate_all();
2420  int ticks2 = SDL_GetTicks();
2421  draw(true,true);
2422  int ticks3 = SDL_GetTicks();
2423  LOG_DP << "invalidate and draw: " << (ticks3 - ticks2) << " and " << (ticks2 - ticks1) << "\n";
2424 
2426 }
2427 
2428 void display::add_redraw_observer(std::function<void(display&)> f)
2429 {
2430  redraw_observers_.push_back(f);
2431 }
2432 
2434 {
2435  redraw_observers_.clear();
2436 }
2437 
2439  draw(true, false);
2440 }
2441 
2442 void display::draw(bool update) {
2443  draw(update, false);
2444 }
2445 
2446 
2447 void display::draw(bool update,bool force) {
2448 // log_scope("display::draw");
2449 
2450  if (screen_.update_locked()) {
2451  return;
2452  }
2453 
2454  if (dirty_) {
2455  flip_locker flip_lock(screen_);
2456  dirty_ = false;
2458  return;
2459  }
2460 
2461  // Trigger cache rebuild when preference gets changed
2464  builder_->rebuild_cache_all();
2465  }
2466 
2468 
2469  draw_init();
2470  pre_draw();
2471  // invalidate all that needs to be invalidated
2473 
2474  if(!get_map().empty()) {
2475  //int simulate_delay = 0;
2476 
2477  /*
2478  * draw_invalidated() also invalidates the halos, so also needs to be
2479  * ran if invalidated_.empty() == true.
2480  */
2481  if(!invalidated_.empty() || preferences::show_haloes()) {
2482  draw_invalidated();
2483  invalidated_.clear();
2484  }
2486  post_commit();
2487  draw_sidebar();
2488 
2489  // Simulate slow PC:
2490  //SDL_Delay(2*simulate_delay + rand() % 20);
2491  }
2492  draw_wrap(update, force);
2493  post_draw();
2494 }
2495 
2497 {
2498  return *map_labels_;
2499 }
2500 
2502 {
2503  return *map_labels_;
2504 }
2505 
2506 const SDL_Rect& display::get_clip_rect()
2507 {
2508  return map_area();
2509 }
2510 
2512 // log_scope("display::draw_invalidated");
2513  SDL_Rect clip_rect = get_clip_rect();
2514  surface& screen = get_screen_surface();
2515  clip_rect_setter set_clip_rect(screen, &clip_rect);
2516  for (const map_location& loc : invalidated_) {
2517  int xpos = get_location_x(loc);
2518  int ypos = get_location_y(loc);
2519 
2520  //const bool on_map = get_map().on_board(loc);
2521  SDL_Rect hex_rect = sdl::create_rect(xpos, ypos, zoom_, zoom_);
2522  if(!sdl::rects_overlap(hex_rect,clip_rect)) {
2523  continue;
2524  }
2525  draw_hex(loc);
2526  drawn_hexes_+=1;
2527  }
2528  invalidated_hexes_ += invalidated_.size();
2529 
2530  if (dc_->teams().empty())
2531  {
2532  // The unit drawer can't function without teams
2533  return;
2534  }
2535 
2536  unit_drawer drawer = unit_drawer(*this);
2537 
2538  for (const map_location& loc : invalidated_) {
2539  unit_map::const_iterator u_it = dc_->units().find(loc);
2541  if (u_it != dc_->units().end()
2542  && (request == exclusive_unit_draw_requests_.end() || request->second == u_it->id()))
2543  drawer.redraw_unit(*u_it);
2544  }
2545 
2546 }
2547 
2549  int xpos = get_location_x(loc);
2550  int ypos = get_location_y(loc);
2551  image::TYPE image_type = get_image_type(loc);
2552  const bool on_map = get_map().on_board(loc);
2553  const time_of_day& tod = get_time_of_day(loc);
2554 
2555  int num_images_fg = 0;
2556  int num_images_bg = 0;
2557 
2558  if(!shrouded(loc)) {
2559  // unshrouded terrain (the normal case)
2560  get_terrain_images(loc, tod.id, BACKGROUND); // updates terrain_image_vector_
2562  num_images_bg = terrain_image_vector_.size();
2563 
2564  get_terrain_images(loc, tod.id, FOREGROUND); // updates terrain_image_vector_
2566  num_images_fg = terrain_image_vector_.size();
2567 
2568  // Draw the grid, if that's been enabled
2569  if(grid_) {
2571  drawing_buffer_add(LAYER_GRID_TOP, loc, xpos, ypos,
2574  drawing_buffer_add(LAYER_GRID_BOTTOM, loc, xpos, ypos,
2575  image::get_image(grid_bottom, image::TOD_COLORED));
2576  }
2577  }
2578 
2579  if(!shrouded(loc)) {
2580  auto it = get_overlays().find(loc);
2581  if(it != get_overlays().end()) {
2582  std::vector<overlay>& overlays = it->second;
2583  if(overlays.size() != 0) {
2584  tod_color tod_col = tod.color + color_adjust_;
2585  image::light_string lt = image::get_light_string(-1, tod_col.r, tod_col.g, tod_col.b);
2586 
2587  for(const overlay& ov : overlays) {
2588  const std::string& current_team_name = get_teams()[viewing_team()].team_name();
2589  const std::vector<std::string>& current_team_names = utils::split(current_team_name);
2590  const std::vector<std::string>& team_names = utils::split(ov.team_name);
2591  if((ov.team_name.empty() ||
2592  std::find_first_of(team_names.begin(), team_names.end(),
2593  current_team_names.begin(), current_team_names.end()) != team_names.end())
2594  && !(fogged(loc) && !ov.visible_in_fog))
2595  {
2596  const surface surf = ov.image.find("~NO_TOD_SHIFT()") == std::string::npos ?
2598  drawing_buffer_add(LAYER_TERRAIN_BG, loc, xpos, ypos, surf);
2599  }
2600  }
2601  }
2602  }
2603  }
2604 
2605  if(!shrouded(loc)) {
2606  // village-control flags.
2607  drawing_buffer_add(LAYER_TERRAIN_BG, loc, xpos, ypos, get_flag(loc));
2608  }
2609 
2610  // Draw the time-of-day mask on top of the terrain in the hex.
2611  // tod may differ from tod if hex is illuminated.
2612  const std::string& tod_hex_mask = tod.image_mask;
2613  if(tod_hex_mask1 != nullptr || tod_hex_mask2 != nullptr) {
2616  } else if(!tod_hex_mask.empty()) {
2617  drawing_buffer_add(LAYER_TERRAIN_FG, loc, xpos, ypos,
2618  image::get_image(tod_hex_mask,image::SCALED_TO_HEX));
2619  }
2620 
2621  // Paint mouseover overlays
2622  if(loc == mouseoverHex_ && (on_map || (in_editor() && get_map().on_board_with_border(loc)))
2623  && mouseover_hex_overlay_ != nullptr) {
2625  }
2626 
2627  // Paint arrows
2628  arrows_map_t::const_iterator arrows_in_hex = arrows_map_.find(loc);
2629  if(arrows_in_hex != arrows_map_.end()) {
2630  for (arrow* const a : arrows_in_hex->second) {
2631  a->draw_hex(loc);
2632  }
2633  }
2634 
2635  // Apply shroud, fog and linger overlay
2636 
2637  if(shrouded(loc)) {
2638  // We apply void also on off-map tiles
2639  // to shroud the half-hexes too
2640  const std::string& shroud_image = get_variant(shroud_images_, loc);
2641  drawing_buffer_add(LAYER_FOG_SHROUD, loc, xpos, ypos,
2642  image::get_image(shroud_image, image_type));
2643  } else if(fogged(loc)) {
2644  const std::string& fog_image = get_variant(fog_images_, loc);
2645  drawing_buffer_add(LAYER_FOG_SHROUD, loc, xpos, ypos,
2646  image::get_image(fog_image, image_type));
2647  }
2648 
2649  if(!shrouded(loc)) {
2650  drawing_buffer_add(LAYER_FOG_SHROUD, loc, xpos, ypos, get_fog_shroud_images(loc, image_type));
2651  }
2652 
2653  if (on_map) {
2654  if (draw_coordinates_) {
2655  int off_x = xpos + hex_size()/2;
2656  int off_y = ypos + hex_size()/2;
2657  surface text = font::get_rendered_text(lexical_cast<std::string>(loc), font::SIZE_SMALL, font::NORMAL_COLOR);
2658  surface bg = create_neutral_surface(text->w, text->h);
2659  SDL_Rect bg_rect {0, 0, text->w, text->h};
2660  sdl::fill_surface_rect(bg, &bg_rect, 0xaa000000);
2661  off_x -= text->w / 2;
2662  off_y -= text->h / 2;
2663  if (draw_terrain_codes_) {
2664  off_y -= text->h / 2;
2665  }
2666  if (draw_num_of_bitmaps_) {
2667  off_y -= text->h / 2;
2668  }
2669  drawing_buffer_add(LAYER_FOG_SHROUD, loc, off_x, off_y, bg);
2670  drawing_buffer_add(LAYER_FOG_SHROUD, loc, off_x, off_y, text);
2671  }
2672  if (draw_terrain_codes_ && (game_config::debug || !shrouded(loc))) {
2673  int off_x = xpos + hex_size()/2;
2674  int off_y = ypos + hex_size()/2;
2675  surface text = font::get_rendered_text(lexical_cast<std::string>(get_map().get_terrain(loc)), font::SIZE_SMALL, font::NORMAL_COLOR);
2676  surface bg = create_neutral_surface(text->w, text->h);
2677  SDL_Rect bg_rect {0, 0, text->w, text->h};
2678  sdl::fill_surface_rect(bg, &bg_rect, 0xaa000000);
2679  off_x -= text->w / 2;
2680  off_y -= text->h / 2;
2682  off_y += text->h / 2;
2683  } else if (draw_num_of_bitmaps_ && !draw_coordinates_) {
2684  off_y -= text->h / 2;
2685  }
2686  drawing_buffer_add(LAYER_FOG_SHROUD, loc, off_x, off_y, bg);
2687  drawing_buffer_add(LAYER_FOG_SHROUD, loc, off_x, off_y, text);
2688  }
2689  if (draw_num_of_bitmaps_) {
2690  int off_x = xpos + hex_size()/2;
2691  int off_y = ypos + hex_size()/2;
2692  surface text = font::get_rendered_text(lexical_cast<std::string>(num_images_bg + num_images_fg), font::SIZE_SMALL, font::NORMAL_COLOR);
2693  surface bg = create_neutral_surface(text->w, text->h);
2694  SDL_Rect bg_rect {0, 0, text->w, text->h};
2695  sdl::fill_surface_rect(bg, &bg_rect, 0xaa000000);
2696  off_x -= text->w / 2;
2697  off_y -= text->h / 2;
2698  if (draw_coordinates_) {
2699  off_y += text->h / 2;
2700  }
2701  if (draw_terrain_codes_) {
2702  off_y += text->h / 2;
2703  }
2704  drawing_buffer_add(LAYER_FOG_SHROUD, loc, off_x, off_y, bg);
2705  drawing_buffer_add(LAYER_FOG_SHROUD, loc, off_x, off_y, text);
2706  }
2707  }
2708 
2709  if(debug_foreground) {
2710  drawing_buffer_add(LAYER_UNIT_DEFAULT, loc, xpos, ypos,
2711  image::get_image("terrain/foreground.png", image_type));
2712  }
2713 
2714 }
2715 
2717  return image::TOD_COLORED;
2718 }
2719 
2720 /*void display::draw_sidebar() {
2721 
2722 }*/
2723 
2724 void display::draw_image_for_report(surface& img, SDL_Rect& rect)
2725 {
2726  SDL_Rect visible_area = get_non_transparent_portion(img);
2727  SDL_Rect target = rect;
2728  if(visible_area.x != 0 || visible_area.y != 0 || visible_area.w != img->w || visible_area.h != img->h) {
2729  if(visible_area.w == 0 || visible_area.h == 0) {
2730  return;
2731  }
2732 
2733  if(visible_area.w > rect.w || visible_area.h > rect.h) {
2734  img.assign(get_surface_portion(img,visible_area));
2735  img.assign(scale_surface(img,rect.w,rect.h));
2736  visible_area.x = 0;
2737  visible_area.y = 0;
2738  visible_area.w = img->w;
2739  visible_area.h = img->h;
2740  } else {
2741  target.x = rect.x + (rect.w - visible_area.w)/2;
2742  target.y = rect.y + (rect.h - visible_area.h)/2;
2743  target.w = visible_area.w;
2744  target.h = visible_area.h;
2745  }
2746 
2747  sdl_blit(img,&visible_area,screen_.getSurface(),&target);
2748  } else {
2749  if(img->w != rect.w || img->h != rect.h) {
2750  img.assign(scale_surface(img,rect.w,rect.h));
2751  }
2752 
2753  sdl_blit(img,nullptr,screen_.getSurface(),&target);
2754  }
2755 }
2756 
2757 /**
2758  * Redraws the specified report (if anything has changed).
2759  * If a config is not supplied, it will be generated via
2760  * reports::generate_report().
2761  */
2762 void display::refresh_report(const std::string& report_name, const config * new_cfg)
2763 {
2764  const theme::status_item *item = theme_.get_status_item(report_name);
2765  if (!item) {
2766  reportSurfaces_[report_name].assign(nullptr);
2767  return;
2768  }
2769 
2770  // Now we will need the config. Generate one if needed.
2771 
2772  boost::optional <events::mouse_handler &> mhb = boost::none;
2773 
2774  if (resources::controller) {
2776  }
2777 
2778  reports::context temp_context = reports::context(*dc_, *this, *resources::tod_manager, wb_.lock(), mhb);
2779 
2780  const config generated_cfg = new_cfg ? config() : reports_object_->generate_report(report_name, temp_context);
2781  if ( new_cfg == nullptr )
2782  new_cfg = &generated_cfg;
2783 
2784  SDL_Rect &rect = reportRects_[report_name];
2785  const SDL_Rect &new_rect = item->location(screen_.screen_area());
2786  surface &surf = reportSurfaces_[report_name];
2787  config &report = reports_[report_name];
2788 
2789  // Report and its location is unchanged since last time. Do nothing.
2790  if (surf && rect == new_rect && report == *new_cfg) {
2791  return;
2792  }
2793 
2794  // Update the config in reports_.
2795  report = *new_cfg;
2796 
2797  if (surf) {
2798  sdl_blit(surf, nullptr, screen_.getSurface(), &rect);
2799  }
2800 
2801  // If the rectangle has just changed, assign the surface to it
2802  if (!surf || new_rect != rect)
2803  {
2804  surf.assign(nullptr);
2805  rect = new_rect;
2806 
2807  // If the rectangle is present, and we are blitting text,
2808  // then we need to backup the surface.
2809  // (Images generally won't need backing up,
2810  // unless they are transparent, but that is done later).
2811  if (rect.w > 0 && rect.h > 0) {
2813  if (reportSurfaces_[report_name] == nullptr) {
2814  ERR_DP << "Could not backup background for report!" << std::endl;
2815  }
2816  }
2817  }
2818 
2820 
2821  if (report.empty()) return;
2822 
2823  int x = rect.x, y = rect.y;
2824 
2825  // Add prefix, postfix elements.
2826  // Make sure that they get the same tooltip
2827  // as the guys around them.
2828  std::string str = item->prefix();
2829  if (!str.empty()) {
2830  config &e = report.add_child_at("element", config(), 0);
2831  e["text"] = str;
2832  e["tooltip"] = report.child("element")["tooltip"];
2833  }
2834  str = item->postfix();
2835  if (!str.empty()) {
2836  config &e = report.add_child("element");
2837  e["text"] = str;
2838  e["tooltip"] = report.child("element", -1)["tooltip"];
2839  }
2840 
2841  // Loop through and display each report element.
2842  int tallest = 0;
2843  int image_count = 0;
2844  bool used_ellipsis = false;
2845  std::ostringstream ellipsis_tooltip;
2846  SDL_Rect ellipsis_area = rect;
2847 
2848  for (config::const_child_itors elements = report.child_range("element");
2849  elements.begin() != elements.end(); elements.pop_front())
2850  {
2851  SDL_Rect area {x, y, rect.w + rect.x - x, rect.h + rect.y - y};
2852  if (area.h <= 0) break;
2853 
2854  std::string t = elements.front()["text"];
2855  if (!t.empty())
2856  {
2857  if (used_ellipsis) goto skip_element;
2858 
2859  // Draw a text element.
2860  font::pango_text text;
2861  if (item->font_rgb_set()) {
2862  text.set_foreground_color(item->font_rgb());
2863  }
2864  bool eol = false;
2865  if (t[t.size() - 1] == '\n') {
2866  eol = true;
2867  t = t.substr(0, t.size() - 1);
2868  }
2869  text.set_font_size(item->font_size());
2870  text.set_text(t, true);
2871  text.set_maximum_width(area.w);
2872  text.set_maximum_height(area.h, false);
2873  surface s = text.render();
2874 
2875  // check if next element is text with almost no space to show it
2876  const int minimal_text = 12; // width in pixels
2877  config::const_child_iterator ee = elements.begin();
2878  if (!eol && rect.w - (x - rect.x + s->w) < minimal_text &&
2879  ++ee != elements.end() && !(*ee)["text"].empty())
2880  {
2881  // make this element longer to trigger rendering of ellipsis
2882  // (to indicate that next elements have not enough space)
2883  //NOTE this space should be longer than minimal_text pixels
2884  t = t + " ";
2885  text.set_text(t, true);
2886  s = text.render();
2887  // use the area of this element for next tooltips
2888  used_ellipsis = true;
2889  ellipsis_area.x = x;
2890  ellipsis_area.y = y;
2891  ellipsis_area.w = s->w;
2892  ellipsis_area.h = s->h;
2893  }
2894 
2895  screen_.blit_surface(x, y, s);
2896  area.w = s->w;
2897  area.h = s->h;
2898  if (area.h > tallest) {
2899  tallest = area.h;
2900  }
2901  if (eol) {
2902  x = rect.x;
2903  y += tallest;
2904  tallest = 0;
2905  } else {
2906  x += area.w;
2907  }
2908  }
2909  else if (!(t = elements.front()["image"].str()).empty())
2910  {
2911  if (used_ellipsis) goto skip_element;
2912 
2913  // Draw an image element.
2914  surface img(image::get_image(t));
2915 
2916  if (!img) {
2917  ERR_DP << "could not find image for report: '" << t << "'" << std::endl;
2918  continue;
2919  }
2920 
2921  if (area.w < img->w && image_count) {
2922  // We have more than one image, and this one doesn't fit.
2924  used_ellipsis = true;
2925  }
2926 
2927  if (img->w < area.w) area.w = img->w;
2928  if (img->h < area.h) area.h = img->h;
2929  draw_image_for_report(img, area);
2930 
2931  ++image_count;
2932  if (area.h > tallest) {
2933  tallest = area.h;
2934  }
2935 
2936  if (!used_ellipsis) {
2937  x += area.w;
2938  } else {
2939  ellipsis_area = area;
2940  }
2941  }
2942  else
2943  {
2944  // No text nor image, skip this element
2945  continue;
2946  }
2947 
2948  skip_element:
2949  t = elements.front()["tooltip"].t_str().base_str();
2950  if (!t.empty()) {
2951  if (!used_ellipsis) {
2952  tooltips::add_tooltip(area, t, elements.front()["help"].t_str().base_str());
2953  } else {
2954  // Collect all tooltips for the ellipsis.
2955  // TODO: need a better separator
2956  // TODO: assign an action
2957  ellipsis_tooltip << t;
2958  config::const_child_iterator ee = elements.begin();
2959  if (++ee != elements.end())
2960  ellipsis_tooltip << "\n _________\n\n";
2961  }
2962  }
2963  }
2964 
2965  if (used_ellipsis) {
2966  tooltips::add_tooltip(ellipsis_area, ellipsis_tooltip.str());
2967  }
2968 }
2969 
2971 {
2972  DBG_DP << "invalidate_all()\n";
2973  invalidateAll_ = true;
2974 #ifdef _OPENMP
2975 #pragma omp critical(invalidated_)
2976 #endif //_OPENMP
2977  invalidated_.clear();
2978 }
2979 
2981 {
2982  if(invalidateAll_)
2983  return false;
2984 
2985  bool tmp;
2986 #ifdef _OPENMP
2987 #pragma omp critical(invalidated_)
2988 #endif //_OPENMP
2989  tmp = invalidated_.insert(loc).second;
2990  return tmp;
2991 }
2992 
2993 bool display::invalidate(const std::set<map_location>& locs)
2994 {
2995  if(invalidateAll_)
2996  return false;
2997  bool ret = false;
2998  for (const map_location& loc : locs) {
2999 #ifdef _OPENMP
3000 #pragma omp critical(invalidated_)
3001 #endif //_OPENMP
3002  ret = invalidated_.insert(loc).second || ret;
3003  }
3004  return ret;
3005 }
3006 
3007 bool display::propagate_invalidation(const std::set<map_location>& locs)
3008 {
3009  if(invalidateAll_)
3010  return false;
3011 
3012  if(locs.size()<=1)
3013  return false; // propagation never needed
3014 
3015  bool result = false;
3016 #ifdef _OPENMP
3017 #pragma omp critical(invalidated_)
3018 #endif //_OPENMP
3019  {
3020  // search the first hex invalidated (if any)
3021  std::set<map_location>::const_iterator i = locs.begin();
3022  for(; i != locs.end() && invalidated_.count(*i) == 0 ; ++i) {}
3023 
3024  if (i != locs.end()) {
3025 
3026  // propagate invalidation
3027  // 'i' is already in, but I suspect that splitting the range is bad
3028  // especially because locs are often adjacents
3029  size_t previous_size = invalidated_.size();
3030  invalidated_.insert(locs.begin(), locs.end());
3031  result = previous_size < invalidated_.size();
3032  }
3033  }
3034  return result;
3035 }
3036 
3038 {
3040 }
3041 
3042 bool display::invalidate_locations_in_rect(const SDL_Rect& rect)
3043 {
3044  if(invalidateAll_)
3045  return false;
3046 
3047  bool result = false;
3048  for (const map_location &loc : hexes_under_rect(rect)) {
3049  result |= invalidate(loc);
3050  }
3051  return result;
3052 }
3053 
3055  if (get_map().is_village(loc)) {
3056  const int owner = dc_->village_owner(loc);
3057  if (owner >= 0 && flags_[owner].need_update()
3058  && (!fogged(loc) || !dc_->teams()[currentTeam_].is_enemy(owner+1))) {
3059  invalidate(loc);
3060  }
3061  }
3062 }
3063 
3065 {
3068  if (animate_map_) {
3069  for (const map_location &loc : get_visible_hexes())
3070  {
3071  if (shrouded(loc)) continue;
3072  if (builder_->update_animation(loc)) {
3073  invalidate(loc);
3074  } else {
3076  }
3077  }
3078  }
3079 
3080  for(const unit& u : dc_->units()) {
3081  u.anim_comp().refresh();
3082  }
3083  for (const unit* u : *fake_unit_man_) {
3084  u->anim_comp().refresh();
3085  }
3086 
3087 
3088  bool new_inval;
3089  do {
3090  new_inval = false;
3091  for (const unit & u : dc_->units()) {
3092  new_inval |= u.anim_comp().invalidate(*this);
3093  }
3094  for (const unit* u : *fake_unit_man_) {
3095  new_inval |= u->anim_comp().invalidate(*this);
3096  }
3097  } while (new_inval);
3098 }
3099 
3101 {
3102  for(const unit & u : dc_->units()) {
3103  u.anim_comp().set_standing();
3104  }
3105 }
3106 
3108 {
3109  const arrow_path_t & arrow_path = arrow.get_path();
3110  for (const map_location& loc : arrow_path)
3111  {
3112  arrows_map_[loc].push_back(&arrow);
3113  }
3114 }
3115 
3117 {
3118  const arrow_path_t & arrow_path = arrow.get_path();
3119  for (const map_location& loc : arrow_path)
3120  {
3121  arrows_map_[loc].remove(&arrow);
3122  }
3123 }
3124 
3126 {
3127  const arrow_path_t & previous_path = arrow.get_previous_path();
3128  for (const map_location& loc : previous_path)
3129  {
3130  arrows_map_[loc].remove(&arrow);
3131  }
3132  const arrow_path_t & arrow_path = arrow.get_path();
3133  for (const map_location& loc : arrow_path)
3134  {
3135  arrows_map_[loc].push_back(&arrow);
3136  }
3137 }
3138 
3140 {
3141  const SDL_Rect& rect = map_area();
3142  return pixel_position_to_hex(xpos_ + rect.x + rect.w / 2 , ypos_ + rect.y + rect.h / 2 );
3143 }
3144 
3145 void display::write(config& cfg) const
3146 {
3147  cfg["view_locked"] = view_locked_;
3148  cfg["color_adjust_red"] = color_adjust_.r;
3149  cfg["color_adjust_green"] = color_adjust_.g;
3150  cfg["color_adjust_blue"] = color_adjust_.b;
3151  get_middle_location().write(cfg.add_child("location"));
3152 }
3153 
3154 void display::read(const config& cfg)
3155 {
3156  view_locked_ = cfg["view_locked"].to_bool(false);
3157  color_adjust_.r = cfg["color_adjust_red"].to_int(0);
3158  color_adjust_.g = cfg["color_adjust_green"].to_int(0);
3159  color_adjust_.b = cfg["color_adjust_blue"].to_int(0);
3160 }
3161 
3163 {
3164  if (!reach_map_changed_) return;
3165  if (reach_map_.empty() != reach_map_old_.empty()) {
3166  // Invalidate everything except the non-darkened tiles
3167  reach_map &full = reach_map_.empty() ? reach_map_old_ : reach_map_;
3168 
3169  for (const auto& hex : get_visible_hexes()) {
3170  reach_map::iterator reach = full.find(hex);
3171  if (reach == full.end()) {
3172  // Location needs to be darkened or brightened
3173  invalidate(hex);
3174  } else if (reach->second != 1) {
3175  // Number needs to be displayed or cleared
3176  invalidate(hex);
3177  }
3178  }
3179  } else if (!reach_map_.empty()) {
3180  // Invalidate only changes
3181  reach_map::iterator reach, reach_old;
3182  for (reach = reach_map_.begin(); reach != reach_map_.end(); ++reach) {
3183  reach_old = reach_map_old_.find(reach->first);
3184  if (reach_old == reach_map_old_.end()) {
3185  invalidate(reach->first);
3186  } else {
3187  if (reach_old->second != reach->second) {
3188  invalidate(reach->first);
3189  }
3190  reach_map_old_.erase(reach_old);
3191  }
3192  }
3193  for (reach_old = reach_map_old_.begin(); reach_old != reach_map_old_.end(); ++reach_old) {
3194  invalidate(reach_old->first);
3195  }
3196  }
3198  reach_map_changed_ = false;
3199 }
3200 
3201 void display::handle_window_event(const SDL_Event& event) {
3202  if (event.type == SDL_WINDOWEVENT) {
3203  switch (event.window.event) {
3204  case SDL_WINDOWEVENT_RESIZED:
3205  case SDL_WINDOWEVENT_RESTORED:
3206  case SDL_WINDOWEVENT_EXPOSED:
3207  dirty_ = true;
3208 
3209  break;
3210  }
3211  }
3212 
3213 
3214 }
3215 
3216 void display::handle_event(const SDL_Event& event) {
3218  return;
3219  }
3220  if (event.type == DRAW_ALL_EVENT) {
3221  draw();
3222  }
3223 }
3224 
3225 display *display::singleton_ = nullptr;
constexpr const T & clamp(const T &value, const T &min, const T &max)
Definition: general.hpp:31
std::shared_ptr< gui::button > find_action_button(const std::string &id)
Retrieves a pointer to a theme UI button.
Definition: display.cpp:804
std::size_t font_size() const
Definition: theme.hpp:109
play_controller * controller
Definition: resources.cpp:21
TYPE
UNSCALED : image will be drawn "as is" without changing size, even in case of redraw SCALED_TO_ZOOM :...
Definition: picture.hpp:190
void drawing_buffer_commit()
Draws the drawing_buffer_ and clears it.
Definition: display.cpp:1257
int village_owner(const map_location &loc) const
Given the location of a village, will return the 0-based index of the team that currently owns it...
void draw_minimap_units()
Definition: display.cpp:1826
surface get_image(const image::locator &i_locator, TYPE type)
function to get the surface corresponding to an image.
Definition: picture.cpp:1022
int zoom_index_
Definition: display.hpp:740
void raise_volatile_undraw_event()
Definition: events.cpp:766
std::string unmoved_color()
Definition: general.cpp:343
void recalculate_shroud()
Definition: label.cpp:277
virtual void pre_draw()
Called near the beginning of each draw() call.
Definition: display.hpp:670
bool discard_previous
An announcement according these options should replace the previous announce (typical of fast announc...
Definition: display.hpp:598
virtual SDL_Rect & location(const SDL_Rect &screen) const
Definition: theme.cpp:317
surface create_neutral_surface(int w, int h)
Definition: utils.cpp:84
draw_layering(const bool auto_join=true)
Definition: video.cpp:49
const std::string & icon() const
Definition: theme.hpp:105
bool is_odd(T num)
Definition: math.hpp:34
virtual void select_hex(map_location hex)
Definition: display.cpp:1590
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:420
::tod_manager * tod_manager
Definition: resources.cpp:29
Arrows destined to be drawn on the map.
int h() const
Effective map height.
Definition: map.hpp:128
reports * reports_object_
Definition: display.hpp:756
unit_iterator end()
Definition: map.hpp:415
bool update_locked() const
Whether the screen has been &#39;locked&#39; or not.
Definition: video.cpp:357
bool minimap_draw_units()
Definition: general.cpp:923
void write(config &cfg) const
Definition: display.cpp:3145
virtual void handle_window_event(const SDL_Event &event)
Definition: display.cpp:3201
hotkey::command_executor * get_hotkey_command_executor() override
Optionally get a command executor to handle context menu events.
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:88
bool show_fps()
Definition: general.cpp:963
const team & get_team(int side) const
void invalidate_game_status()
Function to invalidate the game status displayed on the sidebar.
Definition: display.hpp:291
Small struct to store and manipulate ToD color adjusts.
Definition: time_of_day.hpp:28
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:441
const arrow_path_t & get_path() const
Definition: arrow.cpp:120
#define DefaultZoom
Definition: display.cpp:76
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:97
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:517
void invalidate_animations()
Function to invalidate animated terrains and units which may have changed.
Definition: display.cpp:3064
#define ERR_DP
Definition: display.cpp:69
virtual void draw_sidebar()
Called near the end of a draw operation, derived classes can use this to render a specific sidebar...
Definition: display.hpp:713
bool invalidate(const map_location &loc)
Function to invalidate a specific tile for redrawing.
Definition: display.cpp:2980
color_t font_rgb() const
Definition: theme.hpp:110
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:255
void draw_wrap(bool update, bool force)
Definition: display.cpp:1662
void flip()
Definition: display.cpp:1310
This class represents a single unit of a specific type.
Definition: unit.hpp:99
bool animate_water_
Local version of preferences::animate_water, used to detect when it&#39;s changed.
Definition: display.hpp:794
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:1300
CKey keys_
Definition: display.hpp:788
bool set_text(const std::string &text, const bool markedup)
Sets the text to render.
Definition: text.cpp:284
void set_clip_rect(const SDL_Rect &r)
bool set_zoom(bool increase)
Zooms the display in (true) or out (false).
Definition: display.cpp:1985
void change_display_context(const display_context *dc)
Definition: display.cpp:486
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:570
boost::circular_buffer< unsigned > frametimes_
Definition: display.hpp:767
bool rects_overlap(const SDL_Rect &rect1, const SDL_Rect &rect2)
Tests whether two rectangles overlap.
Definition: rect.cpp:33
int ypos_
Definition: display.hpp:736
void set_team_colors(const std::vector< std::string > *colors)
set the team colors used by the TC image modification use a vector with one string for each team usin...
Definition: picture.cpp:861
bool reach_map_changed_
Definition: display.hpp:1027
static double get_zoom_factor()
Returns the current zoom factor.
Definition: display.hpp:258
drawing_buffer drawing_buffer_
Definition: display.hpp:969
void set_playing_team(std::size_t team)
set_playing_team sets the team whose turn it currently is
Definition: display.cpp:392
surface reverse_image(const surface &surf)
function to reverse an image.
Definition: picture.cpp:1195
void draw_init()
Initiate a redraw.
Definition: display.cpp:1623
void set_default_zoom()
Sets the zoom amount to the default.
Definition: display.cpp:2053
void set_idle_anim_rate(int rate)
Definition: display.cpp:2372
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:585
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
int draw_delay()
Definition: general.cpp:973
Definition: video.hpp:31
bool minimap_movement_coding()
Definition: general.cpp:903
std::map< std::string, SDL_Rect > reportRects_
Definition: display.hpp:774
void draw_all_panels()
redraw all panels associated with the map display
Definition: display.cpp:1444
void render_image(int x, int y, const display::drawing_layer drawing_layer, const map_location &loc, surface image, bool hreverse=false, bool greyscale=false, fixed_t alpha=ftofxp(1.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:1521
bool in_dialog()
Definition: show_dialog.cpp:56
virtual void draw_invalidated()
Only called when there&#39;s actual redrawing to do.
Definition: display.cpp:2511
map_location mouseoverHex_
Definition: display.hpp:787
void flip()
Renders the screen.
Definition: video.cpp:337
void init_flags()
Init the flag list and the team colors used by ~TC.
Definition: display.cpp:273
Manages a list of fake units for the display object.
Mouseover overlay used by editor.
Definition: display.hpp:821
virtual void draw_hex(const map_location &hex)
Definition: arrow.cpp:136
child_itors child_range(config_key_type key)
Definition: config.cpp:362
static void draw_background(surface screen, const SDL_Rect &area, const std::string &image)
Definition: display.cpp:1465
std::string id
Definition: time_of_day.hpp:91
void lock_updates(bool value)
Stop the screen being redrawn.
Definition: video.cpp:348
bool draw_coordinates_
Debug flag - overlay x,y coords on tiles.
Definition: display.hpp:1049
const SDL_Rect & map_area() const
Returns the area used for the map.
Definition: display.cpp:533
int scroll_speed()
Definition: general.cpp:859
void remove_floating_label(int handle)
removes the floating label given by &#39;handle&#39; from the screen
void redraw_everything()
Invalidates entire screen, including all tiles and sidebar.
Definition: display.cpp:2377
static lg::log_domain log_display("display")
int fps_handle_
Handle for the label which displays frames per second.
Definition: display.hpp:1036
bool show_everything() const
Definition: display.hpp:90
The class terrain_builder is constructed from a config object, and a gamemap object.
Definition: builder.hpp:45
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:731
void fill_surface_rect(surface &dst, SDL_Rect *dst_rect, const uint32_t color)
Fill a rectangle on a given surface.
Definition: rect.hpp:114
void reset_halo_manager()
Definition: display.cpp:492
void add_frame(int duration, const T &value, bool force_change=false)
Adds a frame to an animation.
int viewing_side() const
Definition: display.hpp:103
reach_map reach_map_
Definition: display.hpp:1025
bool non_interactive() const
Definition: video.cpp:135
SDL_Rect intersect_rects(const SDL_Rect &rect1, const SDL_Rect &rect2)
Calculates the intersection of two rectangles.
Definition: rect.cpp:39
#define h
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
iterator & operator++()
increment y first, then when reaching bottom, increment x
Definition: display.cpp:637
std::vector< std::function< void(display &)> > redraw_observers_
Definition: display.hpp:1046
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:377
#define MaxZoom
Definition: display.cpp:79
map_location selectedHex_
Definition: display.hpp:786
void set_font_size(int font_size)
const std::vector< std::string > items
CVideo & screen_
Definition: display.hpp:733
surface scale_surface(const surface &surf, int w, int h)
Scale a surface using alpha-weighted modified bilinear filtering Note: causes artifacts with alpha gr...
Definition: utils.cpp:274
static display * singleton_
Definition: display.hpp:1065
void blit_surface(int x, int y, surface surf, SDL_Rect *srcrect=nullptr, SDL_Rect *clip_rect=nullptr)
Draws a surface directly onto the screen framebuffer.
Definition: video.cpp:163
#define d
virtual void draw()
Draws invalidated items.
Definition: display.cpp:2438
surface blend_surface(const surface &surf, const double amount, const color_t color)
Blends a surface with a color.
Definition: utils.cpp:1804
bool animate_water()
Definition: general.cpp:898
const int SIZE_PLUS
Definition: constants.cpp:26
Top half part of grid image.
Definition: display.hpp:820
surface get_surface_portion(const surface &src, SDL_Rect &area)
Get a portion of the screen.
Definition: utils.cpp:2273
void enable_menu(const std::string &item, bool enable)
Finds the menu which has a given item in it, and enables or disables it.
Definition: display.cpp:1732
Rectangular area of hexes, allowing to decide how the top and bottom edges handles the vertical shift...
Definition: display.hpp:301
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:39
const std::vector< label > & labels() const
Definition: theme.hpp:250
-file sdl_utils.hpp
static unsigned int zoom_
Definition: display.hpp:739
void clear_redraw_observers()
Clear the redraw observers.
Definition: display.cpp:2433
const SDL_Rect & minimap_area() const
mapx is the width of the portion of the display which shows the game area.
Definition: display.hpp:214
drawing_layer
The layers to render something on.
Definition: display.hpp:815
virtual const gamemap & map() const =0
std::vector< std::string > split(const std::string &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
std::vector< surface > get_fog_shroud_images(const map_location &loc, image::TYPE image_type)
Definition: display.cpp:940
void reset_standing_animations()
Definition: display.cpp:3100
void remove_arrow(arrow &)
Definition: display.cpp:3116
surface & getSurface()
Returns a reference to the framebuffer.
Definition: video.cpp:472
bool panelsDrawn_
Definition: display.hpp:751
#define DBG_DP
Definition: display.cpp:71
void draw_centered_on_background(surface surf, const SDL_Rect &rect, const color_t &color, surface target)
Definition: utils.cpp:2388
surface tod_hex_mask2
Definition: display.hpp:782
SDL_Rect get_non_transparent_portion(const surface &surf)
Definition: utils.cpp:2319
void init_flags_for_side_internal(std::size_t side, const std::string &side_color)
Definition: display.cpp:300
void update_display()
Copy the backbuffer to the framebuffer.
Definition: display.cpp:1333
std::vector< map_location > arrow_path_t
Definition: arrow.hpp:24
bool null() const
Definition: surface.hpp:79
Unit and team statistics.
very simple iterator to walk into the rect_of_hexes
Definition: display.hpp:308
iterator begin() const
Definition: display.cpp:650
const rect_of_hexes get_visible_hexes() const
Returns the rectangular area of visible hexes.
Definition: display.hpp:338
surface submerge_alpha(const surface &surf, int depth, float alpha_base, float alpha_delta)
Progressively reduce alpha of bottom part of the surface.
Definition: utils.cpp:1336
std::vector< std::shared_ptr< gui::button > > action_buttons_
Definition: display.hpp:777
map_location loc_
surface map_screenshot_surf_
Definition: display.hpp:1044
void clear_tooltips()
Definition: tooltips.cpp:120
virtual bool in_editor() const
Definition: display.hpp:202
bool team_valid() const
Definition: display.cpp:706
#define b
const theme::action * action_pressed()
Definition: display.cpp:1700
bool show_haloes()
Definition: game.cpp:860
const int SIZE_XLARGE
Definition: constants.cpp:29
const std::string & text() const
Definition: theme.hpp:103
std::vector< std::string > shroud_images_
Definition: display.hpp:784
void draw_image_for_report(surface &img, SDL_Rect &rect)
Definition: display.cpp:2724
Object which defines a time of day with associated bonuses, image, sounds etc.
Definition: time_of_day.hpp:57
bool exists(const image::locator &i_locator)
returns true if the given image actually exists, without loading it.
Definition: picture.cpp:1217
void blindfold(bool flag)
Definition: display.cpp:502
static const char * name(const std::vector< SDL_Joystick *> &joysticks, const std::size_t index)
Definition: joystick.cpp:48
events::generic_event complete_redraw_event_
notify observers that the screen has been redrawn completely atm this is used for replay_controller t...
Definition: display.hpp:765
void parse_team_overlays()
Check the overlay_map for proper team-specific overlays to be displayed/hidden.
Definition: display.cpp:92
const config & options()
Definition: game.cpp:568
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:44
surface flop_surface(const surface &surf)
Definition: utils.cpp:2070
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
const std::string & id() const
Gets this unit&#39;s id.
Definition: unit.hpp:343
uint32_t last_frame_finished_
Definition: display.hpp:771
void adjust_surface_alpha(surface &surf, fixed_t amount)
Definition: utils.cpp:1174
void get_terrain_images(const map_location &loc, const std::string &timeid, TERRAIN_TYPE terrain_type)
Definition: display.cpp:1029
const arrow_path_t & get_previous_path() const
Definition: arrow.cpp:125
double turbo_speed() const
Definition: display.cpp:2358
static bool zoom_at_max()
Definition: display.cpp:1975
void draw_minimap()
Definition: display.cpp:1766
light_string get_light_string(int op, int r, int g, int b)
return light_string of one light operation(see above)
Definition: picture.cpp:714
const std::string & prefix() const
Definition: theme.hpp:127
void new_animation_frame()
Definition: animated.cpp:30
static unsigned calculate_fps(unsigned frametime)
Definition: display.cpp:1328
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:561
bool tile_fully_on_screen(const map_location &loc) const
Check if a tile is fully visible on screen.
Definition: display.cpp:2065
const theme::menu * menu_pressed()
Definition: display.cpp:1716
Arrows destined to be drawn on the map.
Definition: arrow.hpp:29
surface screenshot(bool map_screenshot=false)
Capture a (map-)screenshot into a surface.
Definition: display.cpp:761
bool empty() const
Tell if the map is of 0 size.
Definition: map.hpp:186
std::string theme()
Definition: game.cpp:818
void set_lifetime(int lifetime)
bool font_rgb_set() const
Definition: theme.hpp:111
bool invalidate_visible_locations_in_rect(const SDL_Rect &rect)
Definition: display.cpp:3037
void recalculate_minimap()
Schedule the minimap for recalculation.
Definition: display.hpp:616
void scroll_to_xy(int screenxpos, int screenypos, SCROLL_TYPE scroll_type, bool force=true)
Definition: display.cpp:2082
config & add_child_at(config_key_type key, const config &val, unsigned index)
Definition: config.cpp:508
bool valid() const
Definition: location.hpp:93
bool map_screenshot_
Used to indicate to drawing functions that we are doing a map screenshot.
Definition: display.hpp:1010
void draw_rectangle(const SDL_Rect &rect, const color_t &color)
Draw a rectangle outline.
Definition: rect.cpp:57
arrows_map_t arrows_map_
Maps the list of arrows for each location.
Definition: display.hpp:1058
virtual image::TYPE get_image_type(const map_location &loc)
Definition: display.cpp:2716
bool idle_anim() const
Definition: display.hpp:492
const std::unique_ptr< map_labels > map_labels_
Definition: display.hpp:755
int xpos_
Definition: display.hpp:736
config generate_report(const std::string &name, context &ct, bool only_static=false)
Definition: reports.cpp:1652
pango_text & set_font_size(const unsigned font_size)
Definition: text.cpp:333
void update_tod(const time_of_day *tod_override=nullptr)
Applies r,g,b coloring to the map.
Definition: display.cpp:430
void render_buttons()
Definition: display.cpp:910
bool dirty_
Definition: display.hpp:1062
std::string background_image
Definition: theme.hpp:87
bool idle_anim_
Definition: display.hpp:1041
SDL_Rect minimap_location_
Definition: display.hpp:745
double size
Definition: theme.hpp:85
void set_zoom(unsigned int amount)
sets the amount scaled images should be scaled.
Definition: picture.cpp:875
TERRAIN_TYPE
Used as a parameter for the get_terrain_at function.
Definition: builder.hpp:49
display(const display_context *dc, std::weak_ptr< wb::manager > wb, reports &reports_object, const config &theme_cfg, const config &level, bool auto_join=true)
Definition: display.cpp:143
std::string grid_bottom
tod_color color_adjust_
Definition: display.hpp:1060
uint8_t r
Red value.
Definition: color.hpp:177
fake_unit_manager * fake_units
Definition: resources.cpp:30
bool is_enemy(int n) const
Definition: team.hpp:243
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:1492
void bounds_check_position()
Definition: display.cpp:2314
virtual void post_draw()
Called at the very end of each draw() call.
Definition: display.hpp:677
Modify, read and display user preferences.
bool font_rgb_set() const
Definition: theme.hpp:135
bool invalidateGameStatus_
Definition: display.hpp:754
#define MinZoom
Definition: display.cpp:78
void set_position(double xpos, double ypos)
void update_arrow(arrow &a)
Called by arrow objects when they change.
Definition: display.cpp:3125
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:770
virtual const unit_map & units() const =0
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 z_order=0)
Functions to add and remove overlays from locations.
Definition: display.cpp:111
bool animate_map()
Definition: general.cpp:893
std::string shroud_prefix
Definition: game_config.cpp:71
void create_buttons()
Definition: display.cpp:851
const std::string & get_id() const
Definition: theme.hpp:50
std::array< map_location, 6 > adjacent_loc_array_t
Definition: location.hpp:170
const color_t YELLOW_COLOR
static const std::string & get_direction(std::size_t n)
Definition: display.cpp:934
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:659
void invalidate_all()
Function to invalidate all tiles.
Definition: display.cpp:2970
void layout_buttons()
Definition: display.cpp:824
int invalidated_hexes_
Count work done for the debug info displayed under fps.
Definition: display.hpp:1038
bool draw_terrain_codes_
Debug flag - overlay terrain codes on tiles.
Definition: display.hpp:1051
int32_t fixed_t
Definition: math.hpp:292
void pump()
Definition: events.cpp:425
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:716
bool point_in_rect(int x, int y, const SDL_Rect &rect)
Tests whether a point is inside a rectangle.
Definition: rect.cpp:22
static int hex_width()
Function which returns the width of a hex in pixels, up to where the next hex starts.
Definition: display.hpp:249
lu_byte right
Definition: lparser.cpp:1027
std::string flag_rgb
static unsigned int last_zoom_
Definition: display.hpp:741
Fog and shroud.
Definition: display.hpp:844
void set_color_adjustment(int r, int g, int b)
will make all scaled images have these rgb values added to all their pixels.
Definition: picture.cpp:847
std::unique_ptr< halo::manager > halo_man_
Definition: display.hpp:657
#define ftofxp(x)
IN: float or int - OUT: fixed_t.
Definition: math.hpp:297
int idle_anim_rate()
Definition: general.cpp:465
Encapsulates the map of the game.
Definition: location.hpp:42
void set_color(const color_t &color)
void set_team(const team *)
Definition: label.cpp:138
bool shrouded(const map_location &loc) const
Returns true if location (x,y) is covered in shroud.
Definition: display.cpp:711
boost::iterator_range< const_child_iterator > const_child_itors
Definition: config.hpp:238
#define DRAW_ALL_EVENT
Definition: events.hpp:29
void reinit_flags_for_side(std::size_t side)
Rebuild the flag list (not team colors) for a single side.
Definition: display.cpp:290
unit_iterator find(std::size_t id)
Definition: map.cpp:311
void process_reachmap_changes()
Definition: display.cpp:3162
bool faked() const
Definition: video.hpp:60
const border_t & border() const
Definition: theme.hpp:280
Definition: video.cpp:45
void recalculate_labels()
Definition: label.cpp:244
int w() const
Effective map width.
Definition: map.hpp:125
virtual void handle_event(const SDL_Event &)
Definition: display.cpp:3216
surface & get_screen_surface()
return the screen surface or the surface used for map_screenshot.
Definition: display.hpp:199
surface tod_hex_mask1
Definition: display.hpp:782
const color_t BLACK_COLOR
virtual overlay_map & get_overlays()=0
std::string allied_color()
Definition: general.cpp:303
std::size_t i
Definition: function.cpp:933
virtual const std::vector< team > & teams() const =0
int current_refresh_rate() const
Definition: video.hpp:147
bool is_blindfolded() const
Definition: display.cpp:510
bool show_border
Definition: theme.hpp:90
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:2161
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:1750
virtual void highlight_hex(map_location hex)
Definition: display.cpp:1598
Holds options for calls to function &#39;announce&#39; (announce).
Definition: display.hpp:588
std::size_t activeTeam_
Definition: display.hpp:886
const std::string & postfix() const
Definition: theme.hpp:128
std::vector< std::shared_ptr< gui::button > > menu_buttons_
Definition: display.hpp:777
std::string moved_color()
Definition: general.cpp:333
void set_diagnostic(const std::string &msg)
Definition: display.cpp:1605
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:2173
void invalidate_animations_location(const map_location &loc)
Per-location invalidation called by invalidate_animations() Extra game per-location invalidation (vil...
Definition: display.cpp:3054
default layer for drawing units
Definition: display.hpp:826
map_location get_middle_location() const
Definition: display.cpp:3139
static map_location::DIRECTION s
std::vector< std::string > names
Definition: build_info.cpp:56
unsigned int fps_counter_
Definition: display.hpp:768
virtual ~display()
Definition: display.cpp:252
double g
Definition: astarsearch.cpp:64
color_t font_rgb() const
Definition: theme.hpp:134
std::size_t playing_team() const
The playing team is the team whose turn it is.
Definition: display.hpp:97
bool draw_num_of_bitmaps_
Debug flag - overlay number of bitmaps on tiles.
Definition: display.hpp:1053
bool redraw_background_
Definition: display.hpp:747
void read(const config &cfg)
Definition: display.cpp:3154
bool dont_show_all_
Definition: display.hpp:735
int get_location_y(const map_location &loc) const
Definition: display.cpp:726
std::basic_string< signed char > light_string
light_string store colors info of central and adjacent hexes.
Definition: picture.hpp:144
const SDL_Rect & map_outside_area() const
Returns the available area for a map, this may differ from the above.
Definition: display.hpp:237
Helper structure for rendering the terrains.
Definition: display.hpp:929
void undraw_floating_labels(surface screen)
void raise_volatile_draw_event()
Definition: events.cpp:748
surface get_rendered_text(const std::string &str, int size, const color_t &color, int style)
Definition: sdl_ttf.cpp:333
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:152
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:2072
bool invalidate_locations_in_rect(const SDL_Rect &rect)
invalidate all hexes under the rectangle rect (in screen coordinates)
Definition: display.cpp:3042
Layer for the terrain drawn in front of the unit.
Definition: display.hpp:827
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:399
virtual void draw_hex(const map_location &loc)
Redraws a single gamemap location.
Definition: display.cpp:2548
Represents terrains which are to be drawn in front of them.
Definition: builder.hpp:54
static bool zoom_at_min()
Definition: display.cpp:1980
int w
const menu * get_menu_item(const std::string &key) const
Definition: theme.cpp:957
Used for the bottom half part of grid image.
Definition: display.hpp:831
theme theme_
Definition: display.hpp:738
virtual const SDL_Rect & get_clip_rect()
Get the clipping rectangle for drawing.
Definition: display.cpp:2506
const bool & debug
bool view_locked_
Definition: display.hpp:737
surface mouseover_hex_overlay_
Definition: display.hpp:779
const std::vector< action > & actions() const
Definition: theme.hpp:253
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:71
surface greyscale_image(const surface &surf)
Definition: utils.cpp:728
Represents terrains which are to be drawn behind unit sprites.
Definition: builder.hpp:50
Layer for the terrain drawn behind the unit.
Definition: display.hpp:816
config & add_child(config_key_type key)
Definition: config.cpp:476
std::vector< std::string > fog_images_
Definition: display.hpp:783
Text class.
Definition: text.hpp:74
std::chrono::seconds fps_start_
Definition: display.hpp:769
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:72
void set_team(std::size_t team, bool observe=false)
Sets the team controlled by the player using the computer.
Definition: display.cpp:373
surface get_lighted_image(const image::locator &i_locator, const light_string &ls, TYPE type)
function to get the surface corresponding to an image.
Definition: picture.cpp:1095
const status_item * get_status_item(const std::string &item) const
Definition: theme.cpp:916
SDL_Rect create_rect(const int x, const int y, const int w, const int h)
Creates an SDL_Rect with the given dimensions.
Definition: rect.hpp:39
std::map< map_location, unsigned int > reach_map
Definition: display.hpp:1024
bool redrawMinimap_
Definition: display.hpp:746
bool set_resolution(const SDL_Rect &screen)
Definition: theme.cpp:614
std::string partial_color()
Definition: general.cpp:353
Definition: display.hpp:44
void drawing_buffer_add(const drawing_layer layer, const map_location &loc, int x, int y, const surface &surf, const SDL_Rect &clip=SDL_Rect())
Add an item to the drawing buffer.
Definition: display.cpp:1178
pango_text & set_maximum_width(int width)
Definition: text.cpp:366
void assign(SDL_Surface *surf)
Definition: surface.hpp:46
const std::vector< team > & get_teams() const
Definition: display.hpp:94
static color_t get_minimap_color(int side)
Definition: team.cpp:945
reach_map reach_map_old_
Definition: display.hpp:1026
int get_location_x(const map_location &loc) const
Functions to get the on-screen positions of hexes.
Definition: display.cpp:721
const gamemap & get_map() const
Definition: display.hpp:92
events::mouse_handler & get_mouse_handler_base() override
Get a reference to a mouse handler member a derived class uses.
virtual void post_commit()
Hook for actions to take right after draw() calls drawing_buffer_commit No action here by default...
Definition: display.hpp:697
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:662
bool invalidateAll_
Definition: display.hpp:748
void redraw_unit(const unit &u) const
draw a unit.
Definition: drawer.cpp:54
events::generic_event scroll_event_
Event raised when the map is being scrolled.
Definition: display.hpp:759
iterator end() const
Definition: display.cpp:654
#define f
int lifetime
Lifetime measured in frames.
Definition: display.hpp:591
double t
Definition: astarsearch.cpp:64
void reload_map()
Updates internals that cache map size.
Definition: display.cpp:480
color_t rep() const
High-contrast shade, intended for the minimap markers.
Definition: color_range.hpp:96
constexpr const SDL_Rect empty_rect
Definition: rect.hpp:31
std::map< std::string, surface > reportSurfaces_
Definition: display.hpp:775
std::set< map_location > invalidated_
Definition: display.hpp:778
bool find(E event, F functor)
Tests whether an event handler is available.
unsigned int tile_size
Definition: game_config.cpp:68
lu_byte left
Definition: lparser.cpp:1026
void drawing_buffer_clear()
Clears the drawing buffer.
Definition: display.cpp:1295
std::map< std::string, config > reports_
Definition: display.hpp:776
const display_context * dc_
Definition: display.hpp:656
surface brighten_image(const surface &surf, fixed_t amount)
Definition: utils.cpp:1132
std::size_t viewing_team() const
The viewing team is the team currently viewing the game.
Definition: display.hpp:102
surface tile_surface(const surface &surf, int w, int h, bool centered)
Tile a surface.
Definition: utils.cpp:639
SDL_Rect draw_text(surface &dst, const SDL_Rect &area, int size, const color_t &color, const std::string &txt, int x, int y, bool use_tooltips, int style)
Function to draw text on a surface.
surface make_neutral_surface(const surface &surf)
Definition: utils.cpp:72
const color_range & color_info(const std::string &name)
std::vector< surface > terrain_image_vector_
Definition: display.hpp:806
this module manages the cache of images.
double idle_anim_rate_
Definition: display.hpp:1042
void fill_rectangle(const SDL_Rect &rect, const color_t &color)
Draws a filled rectangle.
Definition: rect.cpp:65
int drawn_hexes_
Definition: display.hpp:1039
Standard logging facilities (interface).
double turbo_speed_
Definition: display.hpp:752
static gui::button::TYPE string_to_button_type(std::string type)
Definition: display.cpp:924
int diagnostic_label_
Definition: display.hpp:750
void add_redraw_observer(std::function< void(display &)> f)
Adds a redraw observer, a function object to be called when redraw_everything is used.
Definition: display.cpp:2428
CVideo & video()
Gets the underlying screen object.
Definition: display.hpp:196
surface create_compatible_surface(const surface &surf, int width, int height)
Definition: utils.cpp:2099
std::string ellipsis
std::size_t font_size() const
Definition: theme.hpp:133
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:28
static void draw_label(CVideo &video, surface target, const theme::label &label)
Definition: display.cpp:1405
bool scroll_to_action()
Definition: general.cpp:363
map_labels & labels()
Definition: display.cpp:2496
const std::vector< menu > & menus() const
Definition: theme.hpp:251
#define e
static void fill_images_list(const std::string &prefix, std::vector< std::string > &images)
Definition: display.cpp:447
const std::unique_ptr< fake_unit_manager > fake_unit_man_
Definition: display.hpp:742
#define SmallZoom
Definition: display.cpp:77
#define final_zoom_index
Definition: display.cpp:75
std::vector< animated< image::locator > > flags_
Animated flags for each team.
Definition: display.hpp:802
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:453
surface & render()
Returns the rendered text.
Definition: text.cpp:91
pango_text & set_maximum_height(int height, bool multiline)
Definition: text.cpp:393
#define zoom_levels
Definition: display.cpp:74
std::string team_name
Definition: overlay.hpp:53
void sdl_blit(const surface &src, SDL_Rect *src_rect, surface &dst, SDL_Rect *dst_rect)
Definition: utils.hpp:33
static void draw_panel(CVideo &video, const theme::panel &panel, std::vector< std::shared_ptr< gui::button >> &)
Definition: display.cpp:1384
uint8_t g
Green value.
Definition: color.hpp:180
uint8_t b
Blue value.
Definition: color.hpp:183
virtual const time_of_day & get_time_of_day(const map_location &loc=map_location::null_location()) const
Definition: display.cpp:424
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:92
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:131
SDL_Rect screen_area(bool as_pixels=true) const
Returns the current window renderer area, either in pixels or screen coordinates. ...
Definition: video.cpp:299
surface get_flag(const map_location &loc)
Definition: display.cpp:353
pango_text & set_foreground_color(const color_t &color)
Definition: text.cpp:356
const int font_size
Definition: button.cpp:40
static map_location::DIRECTION n
std::shared_ptr< halo_record > handle
Definition: halo.hpp:31
int add_tooltip(const SDL_Rect &rect, const std::string &message, const std::string &action, bool use_markup, const surface &foreground)
Definition: tooltips.cpp:175
bool scroll(int xmov, int ymov, bool force=false)
Scrolls the display by xmov,ymov pixels.
Definition: display.cpp:1881
bool turbo_
Definition: display.hpp:753
void add_arrow(arrow &)
Definition: display.cpp:3107
void remove_overlay(const map_location &loc)
remove_overlay will remove all overlays on a tile.
Definition: display.cpp:126
bool animate_map_
Local cache for preferences::animate_map, since it is constantly queried.
Definition: display.hpp:791
const SDL_Rect & max_map_area() const
Returns the maximum area used for the map regardless to resolution and view size. ...
Definition: display.cpp:516
static rng & default_instance()
Definition: random.cpp:73
#define LOG_DP
Definition: display.cpp:70
const std::vector< panel > & panels() const
Definition: theme.hpp:249
static void toggle_debug_foreground()
Toggle to debug foreground terrain.
Definition: display.cpp:1305
std::string grid_top
const std::unique_ptr< terrain_builder > builder_
Definition: display.hpp:743
static const std::string & get_variant(const std::vector< std::string > &variants, const map_location &loc)
Definition: display.cpp:469
void invalidate_theme()
Definition: display.hpp:402
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
std::string fog_prefix
Definition: game_config.cpp:71
bool empty() const
Definition: config.cpp:884
void refresh_report(const std::string &report_name, const config *new_cfg=nullptr)
Redraws the specified report (if anything has changed).
Definition: display.cpp:2762
std::string enemy_color()
Definition: general.cpp:323
std::weak_ptr< wb::manager > wb_
Definition: display.hpp:658
std::shared_ptr< gui::button > find_menu_button(const std::string &id)
Definition: display.cpp:814
Definition: display.hpp:48
void set_theme(config theme_cfg)
Definition: display.cpp:262
int blindfold_ctr_
Definition: display.hpp:652
const std::string & team_name() const
Definition: team.hpp:296
void draw_floating_labels(surface screen)
TERRAIN_TYPE
Definition: display.hpp:717
bool grid_
Definition: display.hpp:749
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.
int get_end_time() const
std::string remove_exclusive_draw(const map_location &loc)
Cancels an exclusive draw request.
Definition: display.cpp:412
const int SIZE_SMALL
Definition: constants.cpp:23
const color_t BAD_COLOR
std::size_t currentTeam_
Definition: display.hpp:734
void rebuild_all()
Rebuild all dynamic terrain.
Definition: display.cpp:475
bool propagate_invalidation(const std::set< map_location > &locs)
If this set is partially invalidated, invalidate all its hexes.
Definition: display.cpp:3007
void write(config &cfg) const
Definition: location.cpp:210
surface minimap_
Definition: display.hpp:744
void start_animation(int start_time, bool cycles=false)
Starts an animation cycle.