The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
display.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2003 - 2025
3 by David White <dave@whitevine.net>
4 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY.
12
13 See the COPYING file for more details.
14*/
15
16/**
17 * @file
18 * Routines to set up the display, scroll and zoom the map.
19 */
20
21#include "display.hpp"
22
23#include "arrow.hpp"
24#include "color.hpp"
25#include "draw.hpp"
26#include "draw_manager.hpp"
27#include "fake_unit_manager.hpp"
28#include "filesystem.hpp"
29#include "floating_label.hpp"
31#include "font/text.hpp"
32#include "global.hpp"
33#include "gui/core/event/handler.hpp" // is_in_dialog
35#include "halo.hpp"
37#include "log.hpp"
38#include "map/map.hpp"
39#include "map/label.hpp"
40#include "minimap.hpp"
41#include "overlay.hpp"
42#include "play_controller.hpp" //note: this can probably be refactored out
43#include "reports.hpp"
44#include "resources.hpp"
46#include "synced_context.hpp"
47#include "team.hpp"
48#include "terrain/builder.hpp"
49#include "time_of_day.hpp"
50#include "tooltips.hpp"
51#include "units/unit.hpp"
53#include "units/drawer.hpp"
54#include "units/orb_status.hpp"
55#include "utils/general.hpp"
56#include "video.hpp"
58
59#include <boost/algorithm/string/trim.hpp>
60
61#include <algorithm>
62#include <array>
63#include <cmath>
64#include <iomanip>
65#include <numeric>
66#include <utility>
67
68#ifdef _WIN32
69#include <windows.h>
70#endif
71
72using namespace std::chrono_literals;
73// Includes for bug #17573
74
75static lg::log_domain log_display("display");
76#define ERR_DP LOG_STREAM(err, log_display)
77#define WRN_DP LOG_STREAM(warn, log_display)
78#define LOG_DP LOG_STREAM(info, log_display)
79#define DBG_DP LOG_STREAM(debug, log_display)
80
81// These are macros instead of proper constants so that they auto-update if the game config is reloaded.
82#define zoom_levels (game_config::zoom_levels)
83#define final_zoom_index (static_cast<int>(zoom_levels.size()) - 1)
84#define DefaultZoom (game_config::tile_size)
85#define SmallZoom (DefaultZoom / 2)
86#define MinZoom (zoom_levels.front())
87#define MaxZoom (zoom_levels.back())
88
89namespace {
90 int prevLabel = 0;
91}
92
93unsigned int display::zoom_ = DefaultZoom;
94unsigned int display::last_zoom_ = SmallZoom;
95
96// Returns index of zoom_levels which is closest match to input zoom_level
97// Assumption: zoom_levels is a sorted vector of ascending tile sizes
98static int get_zoom_levels_index(unsigned int zoom_level)
99{
100 zoom_level = std::clamp(zoom_level, MinZoom, MaxZoom); // ensure zoom_level is within zoom_levels bounds
101 auto iter = std::lower_bound(zoom_levels.begin(), zoom_levels.end(), zoom_level);
102
103 // find closest match
104 if(iter != zoom_levels.begin() && iter != zoom_levels.end()) {
105 float diff = *iter - *(iter - 1);
106 float lower = (zoom_level - *(iter - 1)) / diff;
107 float upper = (*iter - zoom_level) / diff;
108
109 // the previous element is closer to zoom_level than the current one
110 if(lower < upper) {
111 iter--;
112 }
113 }
114
115 return std::distance(zoom_levels.begin(), iter);
116}
117
119{
120 std::vector<overlay>& overlays = get_overlays()[loc];
121 auto pos = std::find_if(overlays.begin(), overlays.end(),
122 [new_order = ov.z_order](const overlay& existing) { return existing.z_order > new_order; });
123
124 auto inserted = overlays.emplace(pos, std::move(ov));
125 if(const std::string& halo = inserted->halo; !halo.empty()) {
126 auto [x, y] = get_location_rect(loc).center();
127 inserted->halo_handle = halo_man_.add(x, y, halo, loc);
128 }
129}
130
132{
133 get_overlays().erase(loc);
134}
135
137{
138 get_overlays().clear();
139}
140
141void display::remove_single_overlay(const map_location& loc, const std::string& toDelete)
142{
144 [&toDelete](const overlay& ov) { return ov.image == toDelete || ov.halo == toDelete || ov.id == toDelete; });
145}
146
148 std::weak_ptr<wb::manager> wb,
149 reports& reports_object,
150 const std::string& theme_id,
151 const config& level)
152 : dc_(dc)
153 , halo_man_()
154 , wb_(std::move(wb))
155 , exclusive_unit_draw_requests_()
156 , viewing_team_index_(0)
157 , dont_show_all_(false)
158 , viewport_origin_(0, 0)
159 , view_locked_(false)
160 , theme_(theme::get_theme_config(theme_id.empty() ? prefs::get().theme() : theme_id), video::game_canvas())
161 , zoom_index_(0)
162 , fake_unit_man_(new fake_unit_manager(*this))
163 , builder_(new terrain_builder(level, (dc_ ? &context().map() : nullptr), theme_.border().tile_image, theme_.border().show_border))
164 , minimap_renderer_(nullptr)
165 , minimap_location_()
166 , redraw_background_(false)
167 , invalidateAll_(true)
168 , diagnostic_label_(0)
169 , invalidateGameStatus_(true)
170 , map_labels_(new map_labels(nullptr))
171 , reports_object_(&reports_object)
172 , scroll_event_("scrolled")
173 , reportLocations_()
174 , reportSurfaces_()
175 , reports_()
176 , menu_buttons_()
177 , action_buttons_()
178 , invalidated_()
179 , tod_hex_mask1(nullptr)
180 , tod_hex_mask2(nullptr)
181 , fog_images_()
182 , shroud_images_()
183 , selectedHex_()
184 , mouseoverHex_()
185 , keys_()
186 , animate_map_(true)
187 , animate_water_(true)
188 , flags_()
189 , playing_team_index_(0)
190 , drawing_buffer_()
191 , map_screenshot_(false)
192 , reach_map_()
193 , reach_map_old_()
194 , reach_map_changed_(true)
195 , reach_map_team_index_(0)
196 , invalidated_hexes_(0)
197 , drawn_hexes_(0)
198 , redraw_observers_()
199 , debug_flags_()
200 , arrows_map_()
201 , color_adjust_()
202{
203 //The following assertion fails when starting a campaign
204 assert(singleton_ == nullptr);
205 singleton_ = this;
206
208
209 blindfold_ctr_ = 0;
210
211 read(level.child_or_empty("display"));
212
215
216 unsigned int tile_size = prefs::get().tile_size();
217 if(tile_size < MinZoom || tile_size > MaxZoom)
218 tile_size = DefaultZoom;
221 if(zoom_ != prefs::get().tile_size()) // correct saved tile_size if necessary
222 prefs::get().set_tile_size(zoom_);
223
224 init_flags();
225
226 if(!menu_buttons_.empty() || !action_buttons_.empty()) {
228 }
229
230#ifdef _WIN32
231 // Increase timer resolution to prevent delays getting much longer than they should.
232 timeBeginPeriod(1u);
233#endif
234}
235
237{
238#ifdef _WIN32
239 timeEndPeriod(1u);
240#endif
241
242 singleton_ = nullptr;
243 resources::fake_units = nullptr;
244}
245
246void display::set_theme(const std::string& new_theme)
247{
249 builder_->set_draw_border(theme_.border().show_border);
250 menu_buttons_.clear();
251 action_buttons_.clear();
253 rebuild_all();
255}
256
258{
259 flags_.clear();
260 if (!dc_) return;
261 flags_.resize(context().teams().size());
262
263 for(const team& t : context().teams()) {
265 }
266}
267
269{
270 std::string flag = t.flag();
271 std::string old_rgb = game_config::flag_rgb;
272 std::string new_rgb = t.color();
273
274 if(flag.empty()) {
276 }
277
278 LOG_DP << "Adding flag for side " << t.side() << " from animation " << flag;
279
280 // Must recolor flag image
281 animated<image::locator> temp_anim;
282
283 for(const std::string& item : utils::square_parenthetical_split(flag)) {
284 const std::vector<std::string> sub_items = utils::split(item, ':');
285 std::string img_path = item;
286 auto time = 100ms;
287
288 if(sub_items.size() > 1) {
289 img_path = sub_items.front();
290 try {
291 time = std::max(1ms, std::chrono::milliseconds{std::stoi(sub_items.back())});
292 } catch(const std::invalid_argument&) {
293 ERR_DP << "Invalid time value found when constructing flag for side " << t.side() << ": " << sub_items.back();
294 }
295 }
296
297 image::locator flag_image(img_path, formatter{} << "~RC(" << old_rgb << ">" << new_rgb << ")");
298 temp_anim.add_frame(time, flag_image);
299 }
300
301 animated<image::locator>& f = flags_[t.side() - 1];
302 f = temp_anim;
303 auto time = f.get_end_time();
304 if (time > 0ms) {
305 int start_time = randomness::rng::default_instance().get_random_int(0, time.count() - 1);
306 f.start_animation(std::chrono::milliseconds{start_time}, true);
307 } else {
308 // this can happen if both flag and game_config::images::flag are empty.
309 ERR_DP << "missing flag for side " << t.side();
310 }
311}
312
314{
315 for(const team& t : context().teams()) {
316 if(t.owns_village(loc) && (!fogged(loc) || !viewing_team().is_enemy(t.side()))) {
317 auto& flag = flags_[t.side() - 1];
318 if(flag.need_update()) {
319 flag.advance_to_current_frame();
320 }
321 const image::locator& image_flag = animate_map_
322 ? flag.get_current_frame()
323 : flag.get_first_frame();
324
325 return image::get_texture(image_flag, image::TOD_COLORED);
326 }
327 }
328
329 return texture();
330}
331
333{
334 return context().teams()[playing_team_index()];
335}
336
338{
339 return context().teams()[viewing_team_index()];
340}
341
342void display::set_viewing_team_index(std::size_t teamindex, bool show_everything)
343{
344 assert(teamindex < context().teams().size());
345 viewing_team_index_ = teamindex;
346 if(!show_everything) {
347 labels().set_team(&context().teams()[teamindex]);
348 dont_show_all_ = true;
349 } else {
350 labels().set_team(nullptr);
351 dont_show_all_ = false;
352 }
354 if(std::shared_ptr<wb::manager> w = wb_.lock()) {
355 w->on_viewer_change(teamindex);
356 }
357}
358
359void display::set_playing_team_index(std::size_t teamindex)
360{
361 assert(teamindex < context().teams().size());
362 playing_team_index_ = teamindex;
364}
365
367{
368 if(!loc.valid()) return false;
369 auto [iter, success] = exclusive_unit_draw_requests_.emplace(loc, unit.id());
370 return success;
371}
372
374{
375 if(!loc.valid()) return {};
376 std::string id = exclusive_unit_draw_requests_[loc];
378 return id;
379}
380
382{
383 auto request = exclusive_unit_draw_requests_.find(loc);
384 return request == exclusive_unit_draw_requests_.end() || request->second == unit.id();
385}
386
387void display::update_tod(const time_of_day* tod_override)
388{
389 const time_of_day* tod = tod_override;
390 if(tod == nullptr) {
391 tod = &get_time_of_day();
392 }
393
394 const tod_color col = color_adjust_ + tod->color;
395 image::set_color_adjustment(col.r, col.g, col.b);
396
398}
399
400void display::adjust_color_overlay(int r, int g, int b)
401{
402 color_adjust_ = tod_color(r, g, b);
403 update_tod();
404}
405
406void display::fill_images_list(const std::string& prefix, std::vector<std::string>& images)
407{
408 if(prefix == ""){
409 return;
410 }
411
412 // search prefix.png, prefix1.png, prefix2.png ...
413 for(int i=0; ; ++i){
414 std::ostringstream s;
415 s << prefix;
416 if(i != 0)
417 s << i;
418 s << ".png";
419 if(image::exists(s.str()))
420 images.push_back(s.str());
421 else if(i>0)
422 break;
423 }
424 if (images.empty())
425 images.emplace_back();
426}
427
429{
430 builder_->rebuild_all();
431}
432
434{
435 redraw_background_ = true;
436 builder_->reload_map();
437}
438
440{
441 dc_ = dc;
442 builder_->change_map(&context().map()); //TODO: Should display_context own and initialize the builder object?
443}
444
446{
447 if(value == true)
449 else
451}
452
454{
455 return blindfold_ctr_ > 0;
456}
457
462
467
472
474{
475 rect max_area{0, 0, 0, 0};
476
477 // hex_size() is always a multiple of 4
478 // and hex_width() a multiple of 3,
479 // so there shouldn't be off-by-one-errors
480 // due to rounding.
481 // To display a hex fully on screen,
482 // a little bit extra space is needed.
483 // Also added the border two times.
484 max_area.w = static_cast<int>((context().map().w() + 2 * theme_.border().size + 1.0 / 3.0) * hex_width());
485 max_area.h = static_cast<int>((context().map().h() + 2 * theme_.border().size + 0.5) * hex_size());
486
487 return max_area;
488}
489
491{
492 rect max_area = max_map_area();
493
494 // if it's for map_screenshot, maximize and don't recenter
495 if(map_screenshot_) {
496 return max_area;
497 }
498
499 rect res = map_outside_area();
500
501 if(max_area.w < res.w) {
502 // map is smaller, center
503 res.x += (res.w - max_area.w) / 2;
504 res.w = max_area.w;
505 }
506
507 if(max_area.h < res.h) {
508 // map is smaller, center
509 res.y += (res.h - max_area.h) / 2;
510 res.h = max_area.h;
511 }
512
513 return res;
514}
515
517{
518 if(map_screenshot_) {
519 return max_map_area();
520 } else {
522 }
523}
524
525bool display::outside_area(const rect& area, const int x, const int y)
526{
527 const int x_thresh = hex_size();
528 const int y_thresh = hex_size();
529 return (x < area.x || x > area.x + area.w - x_thresh || y < area.y || y > area.y + area.h - y_thresh);
530}
531
532// This function uses the screen as reference
533map_location display::hex_clicked_on(int xclick, int yclick) const
534{
535 rect r = map_area();
536 if(!r.contains(xclick, yclick)) {
537 return map_location();
538 }
539
540 xclick -= r.x;
541 yclick -= r.y;
542
543 return pixel_position_to_hex(viewport_origin_.x + xclick, viewport_origin_.y + yclick);
544}
545
546// This function uses the rect of map_area as reference
548{
549 // adjust for the border
550 x -= static_cast<int>(theme_.border().size * hex_width());
551 y -= static_cast<int>(theme_.border().size * hex_size());
552 // The editor can modify the border and this will result in a negative y
553 // value. Instead of adding extra cases we just shift the hex. Since the
554 // editor doesn't use the direction this is no problem.
555 const int offset = y < 0 ? 1 : 0;
556 if(offset) {
557 x += hex_width();
558 y += hex_size();
559 }
560 const int s = hex_size();
561 const int tesselation_x_size = hex_width() * 2;
562 const int tesselation_y_size = s;
563 const int x_base = x / tesselation_x_size * 2;
564 const int x_mod = x % tesselation_x_size;
565 const int y_base = y / tesselation_y_size;
566 const int y_mod = y % tesselation_y_size;
567
568 int x_modifier = 0;
569 int y_modifier = 0;
570
571 if (y_mod < tesselation_y_size / 2) {
572 if ((x_mod * 2 + y_mod) < (s / 2)) {
573 x_modifier = -1;
574 y_modifier = -1;
575 } else if ((x_mod * 2 - y_mod) < (s * 3 / 2)) {
576 x_modifier = 0;
577 y_modifier = 0;
578 } else {
579 x_modifier = 1;
580 y_modifier = -1;
581 }
582
583 } else {
584 if ((x_mod * 2 - (y_mod - s / 2)) < 0) {
585 x_modifier = -1;
586 y_modifier = 0;
587 } else if ((x_mod * 2 + (y_mod - s / 2)) < s * 2) {
588 x_modifier = 0;
589 y_modifier = 0;
590 } else {
591 x_modifier = 1;
592 y_modifier = 0;
593 }
594 }
595
596 return map_location(x_base + x_modifier - offset, y_base + y_modifier - offset);
597}
598
600{
601 if (loc_.y < rect_.bottom[loc_.x & 1])
602 ++loc_.y;
603 else {
604 ++loc_.x;
605 loc_.y = rect_.top[loc_.x & 1];
606 }
607
608 return *this;
609}
610
611// begin is top left, and end is after bottom right
620
622{
623 if(r.w <= 0 || r.h <= 0) {
624 // Dummy values giving begin == end (end is right + 1)
625 return {0, -1, {0, 0}, {0, 0}};
626 }
627
628 // translate rect coordinates from screen-based to map_area-based
629 auto [x, y] = viewport_origin_ - map_area().origin() + r.origin();
630 // we use the "double" type to avoid important rounding error (size of an hex!)
631 // we will also need to use std::floor to avoid bad rounding at border (negative values)
632 double tile_width = hex_width();
633 double tile_size = hex_size();
634 double border = theme_.border().size;
635
636 return {
637 // we minus "0.(3)", for horizontal imbrication.
638 // reason is: two adjacent hexes each overlap 1/4 of their width, so for
639 // grid calculation 3/4 of tile width is used, which by default gives
640 // 18/54=0.(3). Note that, while tile_width is zoom dependent, 0.(3) is not.
641 static_cast<int>(std::floor(-border + x / tile_width - 0.3333333)),
642
643 // we remove 1 pixel of the rectangle dimensions
644 // (the rounded division take one pixel more than needed)
645 static_cast<int>(std::floor(-border + (x + r.w - 1) / tile_width)),
646
647 // for odd x, we must shift up one half-hex. Since x will vary along the edge,
648 // we store here the y values for even and odd x, respectively
649 {
650 static_cast<int>(std::floor(-border + y / tile_size)),
651 static_cast<int>(std::floor(-border + y / tile_size - 0.5))
652 },
653 {
654 static_cast<int>(std::floor(-border + (y + r.h - 1) / tile_size)),
655 static_cast<int>(std::floor(-border + (y + r.h - 1) / tile_size - 0.5))
656 }
657 };
658
659 // TODO: in some rare cases (1/16), a corner of the big rect is on a tile
660 // (the 72x72 rectangle containing the hex) but not on the hex itself
661 // Can maybe be optimized by using pixel_position_to_hex
662}
663
665{
667}
668
670{
672}
673
675{
676 // Two possible regressions to be aware of when changing this code:
677 // https://github.com/wesnoth/wesnoth/issues/10903 (faulty hex offset) and
678 // https://github.com/wesnoth/wesnoth/issues/10676 (Grid overlay flickering)
679 return {
680 map_area().x - viewport_origin_.x + static_cast<int>(std::ceil((loc.x + theme_.border().size) * hex_width())),
681 map_area().y - viewport_origin_.y + static_cast<int>(std::ceil((loc.y + theme_.border().size) * zoom_ + (is_odd(loc.x) ? zoom_ / 2.0 : 0.0)))
682 };
683}
684
686{
687 // TODO: evaluate how these functions should be defined in terms of each other
688 return { get_location(loc), point{hex_size(), hex_size()} };
689}
690
692{
693 // TODO: don't return location for this,
694 // instead directly scroll to the clicked pixel position
695
696 if(!minimap_area().contains(x, y)) {
697 return map_location();
698 }
699
700 // we transform the coordinates from minimap to the full map image
701 // probably more adjustments to do (border, minimap shift...)
702 // but the mouse and human capacity to evaluate the rectangle center
703 // is not pixel precise.
704 int px = (x - minimap_location_.x) * context().map().w() * hex_width() / std::max(minimap_location_.w, 1);
705 int py = (y - minimap_location_.y) * context().map().h() * hex_size() / std::max(minimap_location_.h, 1);
706
708 if(loc.x < 0) {
709 loc.x = 0;
710 } else if(loc.x >= context().map().w()) {
711 loc.x = context().map().w() - 1;
712 }
713
714 if(loc.y < 0) {
715 loc.y = 0;
716 } else if(loc.y >= context().map().h()) {
717 loc.y = context().map().h() - 1;
718 }
719
720 return loc;
721}
722
723surface display::screenshot(bool map_screenshot)
724{
725 if (!map_screenshot) {
726 LOG_DP << "taking ordinary screenshot";
727 return video::read_pixels();
728 }
729
730 if (context().map().empty()) {
731 ERR_DP << "No map loaded, cannot create a map screenshot.";
732 return nullptr;
733 }
734
735 // back up the current map view position and move to top-left
736 point old_pos = viewport_origin_;
737 viewport_origin_ = {0, 0};
738
739 // Reroute render output to a separate texture until the end of scope.
740 rect area = max_map_area();
741 if (area.w > 1 << 16 || area.h > 1 << 16) {
742 WRN_DP << "Excessively large map screenshot area";
743 }
744 LOG_DP << "creating " << area.w << " by " << area.h
745 << " texture for map screenshot";
746 texture output_texture(area.w, area.h, SDL_TEXTUREACCESS_TARGET);
747 auto target_setter = draw::set_render_target(output_texture);
748 auto clipper = draw::override_clip(area);
749
750 map_screenshot_ = true;
751
752 DBG_DP << "invalidating region for map screenshot";
754
755 DBG_DP << "drawing map screenshot";
756 draw();
757
758 map_screenshot_ = false;
759
760 // Restore map viewport position
761 viewport_origin_ = old_pos;
762
763 // Read rendered pixels back as an SDL surface.
764 LOG_DP << "reading pixels for map screenshot";
765 return video::read_pixels();
766}
767
768std::shared_ptr<gui::button> display::find_action_button(const std::string& id)
769{
770 for(auto& b : action_buttons_) {
771 if(b->id() == id) {
772 return b;
773 }
774 }
775 return nullptr;
776}
777
778std::shared_ptr<gui::button> display::find_menu_button(const std::string& id)
779{
780 for(auto& b : menu_buttons_) {
781 if(b->id() == id) {
782 return b;
783 }
784 }
785 return nullptr;
786}
787
789{
790 DBG_DP << "positioning menu buttons...";
791 for(const auto& menu : theme_.menus()) {
792 if(auto b = find_menu_button(menu.get_id())) {
793 const rect& loc = menu.location(video::game_canvas());
794 b->set_location(loc);
795 b->set_measurements(0,0);
796 b->set_label(menu.title());
797 b->set_image(menu.image());
798 }
799 }
800
801 DBG_DP << "positioning action buttons...";
802 for(const auto& action : theme_.actions()) {
803 if(auto b = find_action_button(action.get_id())) {
804 const rect& loc = action.location(video::game_canvas());
805 b->set_location(loc);
806 b->set_measurements(0,0);
807 b->set_label(action.title());
808 b->set_image(action.image());
809 }
810 }
811}
812
813namespace
814{
815gui::button::TYPE string_to_button_type(const std::string& type)
816{
817 if(type == "checkbox") {
819 } else if(type == "image") {
821 } else if(type == "radiobox") {
823 } else if(type == "turbo") {
825 } else {
827 }
828}
829} // namespace
830
832{
833 if(video::headless()) {
834 return;
835 }
836
837 // Keep the old buttons around until we're done so we can check the previous state.
838 std::vector<std::shared_ptr<gui::button>> menu_work;
839 std::vector<std::shared_ptr<gui::button>> action_work;
840
841 DBG_DP << "creating menu buttons...";
842 for(const auto& menu : theme_.menus()) {
843 if(!menu.is_button()) {
844 continue;
845 }
846
847 auto b = std::make_shared<gui::button>(menu.title(), gui::button::TYPE_PRESS, menu.image(),
849
850 DBG_DP << "drawing button " << menu.get_id();
851 b->set_id(menu.get_id());
852 if(!menu.tooltip().empty()) {
853 b->set_tooltip_string(menu.tooltip());
854 }
855
856 if(auto b_prev = find_menu_button(b->id())) {
857 b->enable(b_prev->enabled());
858 }
859
860 menu_work.push_back(std::move(b));
861 }
862
863 DBG_DP << "creating action buttons...";
864 for(const auto& action : theme_.actions()) {
865 auto b = std::make_shared<gui::button>(action.title(), string_to_button_type(action.type()),
866 action.image(), gui::button::DEFAULT_SPACE, true, action.overlay(), font::SIZE_BUTTON_SMALL);
867
868 DBG_DP << "drawing button " << action.get_id();
869 b->set_id(action.get_id());
870 if(!action.tooltip(0).empty()) {
871 b->set_tooltip_string(action.tooltip(0));
872 }
873
874 if(auto b_prev = find_action_button(b->id())) {
875 b->enable(b_prev->enabled());
876 if(b_prev->get_type() == gui::button::TYPE_CHECK) {
877 b->set_check(b_prev->checked());
878 }
879 }
880
881 action_work.push_back(std::move(b));
882 }
883
884 menu_buttons_ = std::move(menu_work);
885 action_buttons_ = std::move(action_work);
886
887 if (prevent_draw_) {
888 // buttons start hidden in this case
889 hide_buttons();
890 }
891
893 DBG_DP << "buttons created";
894}
895
897{
898 // This is currently unnecessary because every GUI1 widget is a TLD.
899 // They will draw themselves. Keeping code in case this changes.
900 return;
901
902 //const rect clip = draw::get_clip();
903 //for(auto& btn : menu_buttons_) {
904 // if(clip.overlaps(btn->location())) {
905 // btn->set_dirty(true);
906 // btn->draw();
907 // }
908 //}
909
910 //for(auto& btn : action_buttons_) {
911 // if(clip.overlaps(btn->location())) {
912 // btn->set_dirty(true);
913 // btn->draw();
914 // }
915 //}
916}
917
919{
920 for (auto& button : menu_buttons_) {
921 button->hide();
922 }
923 for (auto& button : action_buttons_) {
924 button->hide();
925 }
926}
927
929{
930 for (auto& button : menu_buttons_) {
931 button->hide(false);
932 }
933 for (auto& button : action_buttons_) {
934 button->hide(false);
935 }
936}
937
938std::vector<texture> display::get_fog_shroud_images(const map_location& loc, image::TYPE image_type)
939{
940 std::vector<std::string> names;
941 const auto adjacent = get_adjacent_tiles(loc);
942
943 enum visibility { FOG = 0, SHROUD = 1, CLEAR = 2 };
944 std::array<visibility, 6> tiles;
945
946 const std::array image_prefix{&game_config::fog_prefix, &game_config::shroud_prefix};
947
948 for(int i = 0; i < 6; ++i) {
949 if(shrouded(adjacent[i])) {
950 tiles[i] = SHROUD;
951 } else if(!fogged(loc) && fogged(adjacent[i])) {
952 tiles[i] = FOG;
953 } else {
954 tiles[i] = CLEAR;
955 }
956 }
957
958 for(int v = FOG; v != CLEAR; ++v) {
959 // Find somewhere that doesn't have overlap to use as a starting point
960 int start{0};
961 while(start < 6 && tiles[start] == v) {
962 ++start;
963 }
964
965 if(start == 6) {
966 // Completely surrounded by fog or shroud. This might have
967 // a special graphic.
968 const std::string name = *image_prefix[v] + "-all.png";
969 if(image::exists(name)) {
970 names.push_back(name);
971 // Proceed to the next visibility (fog -> shroud -> clear).
972 continue;
973 }
974 // No special graphic found. We'll just combine some other images
975 // and hope it works out.
976 start = 0;
977 }
978
979 // Find all the directions overlap occurs from
980 for(int i = (start + 1) % 6, cap1 = 0; i != start && cap1 != 6; ++cap1) {
981 if(tiles[i] == v) {
982 std::ostringstream stream;
983 std::string name;
984 stream << *image_prefix[v];
985
986 for(int cap2 = 0; v == tiles[i] && cap2 != 6; i = (i + 1) % 6, ++cap2) {
988
989 if(!image::exists(stream.str() + ".png")) {
990 // If we don't have any surface at all,
991 // then move onto the next overlapped area
992 if(name.empty()) {
993 i = (i + 1) % 6;
994 }
995 break;
996 } else {
997 name = stream.str();
998 }
999 }
1000
1001 if(!name.empty()) {
1002 names.push_back(name + ".png");
1003 }
1004 } else {
1005 i = (i + 1) % 6;
1006 }
1007 }
1008 }
1009
1010 // now get the textures
1011 std::vector<texture> res;
1012
1013 for(const std::string& name : names) {
1014 if(texture tex = image::get_texture(name, image_type)) {
1015 res.push_back(std::move(tex));
1016 }
1017 }
1018
1019 return res;
1020}
1021
1023{
1024 terrain_image_vector_.clear();
1025
1026 std::vector<image::light_adjust> lighting;
1027 const time_of_day& tod = get_time_of_day(loc);
1028
1029 // get all the light transitions
1030 const auto adjs = get_adjacent_tiles(loc);
1031 std::array<const time_of_day*, adjs.size()> atods;
1032
1033 for(std::size_t d = 0; d < adjs.size(); ++d) {
1034 atods[d] = &get_time_of_day(adjs[d]);
1035 }
1036
1037 for(int d = 0; d < 6; ++d) {
1038 /*
1039 concave
1040 _____
1041 / \
1042 / atod1 \_____
1043 \ !tod / \
1044 \_____/ atod2 \
1045 / \__\ !tod /
1046 / \_____/
1047 \ tod /
1048 \_____/
1049 */
1050
1051 const time_of_day& atod1 = *atods[d];
1052 const time_of_day& atod2 = *atods[(d + 1) % 6];
1053
1054 if(atod1.color == tod.color || atod2.color == tod.color || atod1.color != atod2.color) {
1055 continue;
1056 }
1057
1058 if(lighting.empty()) {
1059 // color the full hex before adding transitions
1060 tod_color col = tod.color + color_adjust_;
1061 lighting.emplace_back(0, col.r, col.g, col.b);
1062 }
1063
1064 // add the directional transitions
1065 tod_color acol = atod1.color + color_adjust_;
1066 lighting.emplace_back(d + 1, acol.r, acol.g, acol.b);
1067 }
1068
1069 for(int d = 0; d < 6; ++d) {
1070 /*
1071 convex 1
1072 _____
1073 / \
1074 / atod1 \_____
1075 \ !tod / \
1076 \_____/ atod2 \
1077 / \__\ tod /
1078 / \_____/
1079 \ tod /
1080 \_____/
1081 */
1082
1083 const time_of_day& atod1 = *atods[d];
1084 const time_of_day& atod2 = *atods[(d + 1) % 6];
1085
1086 if(atod1.color == tod.color || atod1.color == atod2.color) {
1087 continue;
1088 }
1089
1090 if(lighting.empty()) {
1091 // color the full hex before adding transitions
1092 tod_color col = tod.color + color_adjust_;
1093 lighting.emplace_back(0, col.r, col.g, col.b);
1094 }
1095
1096 // add the directional transitions
1097 tod_color acol = atod1.color + color_adjust_;
1098 lighting.emplace_back(d + 7, acol.r, acol.g, acol.b);
1099 }
1100
1101 for(int d = 0; d < 6; ++d) {
1102 /*
1103 convex 2
1104 _____
1105 / \
1106 / atod1 \_____
1107 \ tod / \
1108 \_____/ atod2 \
1109 / \__\ !tod /
1110 / \_____/
1111 \ tod /
1112 \_____/
1113 */
1114
1115 const time_of_day& atod1 = *atods[d];
1116 const time_of_day& atod2 = *atods[(d + 1) % 6];
1117
1118 if(atod2.color == tod.color || atod1.color == atod2.color) {
1119 continue;
1120 }
1121
1122 if(lighting.empty()) {
1123 // color the full hex before adding transitions
1124 tod_color col = tod.color + color_adjust_;
1125 lighting.emplace_back(0, col.r, col.g, col.b);
1126 }
1127
1128 // add the directional transitions
1129 tod_color acol = atod2.color + color_adjust_;
1130 lighting.emplace_back(d + 13, acol.r, acol.g, acol.b);
1131 }
1132
1133 if(lighting.empty()){
1134 tod_color col = tod.color + color_adjust_;
1135 if(!col.is_zero()){
1136 // no real lightmap needed but still color the hex
1137 lighting.emplace_back(-1, col.r, col.g, col.b);
1138 }
1139 }
1140
1141 const terrain_builder::TERRAIN_TYPE builder_terrain_type = terrain_type == FOREGROUND
1144
1145 if(const terrain_builder::imagelist* const terrains = builder_->get_terrain_at(loc, timeid, builder_terrain_type)) {
1146 // Cache the offmap name. Since it is themeable it can change, so don't make it static.
1147 const std::string off_map_name = "terrain/" + theme_.border().tile_image;
1148 for(const auto& terrain : *terrains) {
1149 const image::locator& image = animate_map_ ? terrain.get_current_frame() : terrain.get_first_frame();
1150
1151 // We prevent ToD coloring and brightening of off-map tiles,
1152 // We need to test for the tile to be rendered and
1153 // not the location, since the transitions are rendered
1154 // over the offmap-terrain and these need a ToD coloring.
1155 texture tex;
1156 const bool off_map = (image.get_filename() == off_map_name
1157 || image.get_modifications().find("NO_TOD_SHIFT()") != std::string::npos);
1158
1159 if(off_map) {
1161 } else if(lighting.empty()) {
1163 } else {
1164 tex = image::get_lighted_texture(image, lighting);
1165 }
1166
1167 if(tex) {
1168 terrain_image_vector_.push_back(std::move(tex));
1169 }
1170 }
1171 }
1172}
1173
1174namespace
1175{
1176constexpr std::array layer_groups {
1180 drawing_layer::reachmap_darken // Make sure the movement doesn't show above fog and reachmap.
1181};
1182
1183enum {
1184 // you may adjust the following when needed:
1185
1186 // maximum border. 3 should be safe even if a larger border is in use somewhere
1187 MAX_BORDER = 3,
1188
1189 // store x, y, and layer in one 32 bit integer
1190 // 4 most significant bits == layer group => 16
1191 BITS_FOR_LAYER_GROUP = 4,
1192
1193 // 10 second most significant bits == y => 1024
1194 BITS_FOR_Y = 10,
1195
1196 // 1 third most significant bit == x parity => 2
1197 BITS_FOR_X_PARITY = 1,
1198
1199 // 8 fourth most significant bits == layer => 256
1200 BITS_FOR_LAYER = 8,
1201
1202 // 9 least significant bits == x / 2 => 512 (really 1024 for x)
1203 BITS_FOR_X_OVER_2 = 9,
1204
1205 SHIFT_LAYER = BITS_FOR_X_OVER_2,
1206
1207 SHIFT_X_PARITY = BITS_FOR_LAYER + SHIFT_LAYER,
1208
1209 SHIFT_Y = BITS_FOR_X_PARITY + SHIFT_X_PARITY,
1210
1211 SHIFT_LAYER_GROUP = BITS_FOR_Y + SHIFT_Y
1212};
1213
1214uint32_t generate_hex_key(const drawing_layer layer, const map_location& loc)
1215{
1216 // Start with the index of last group entry...
1217 uint32_t group_i = layer_groups.size() - 1;
1218
1219 // ...and works backwards until the group containing the specified layer is found.
1220 while(layer < layer_groups[group_i]) {
1221 --group_i;
1222 }
1223
1224 // the parity of x must be more significant than the layer but less significant than y.
1225 // Thus basically every row is split in two: First the row containing all the odd x
1226 // then the row containing all the even x. Since thus the least significant bit of x is
1227 // not required for x ordering anymore it can be shifted out to the right.
1228 const uint32_t x_parity = static_cast<uint32_t>(loc.x) & 1;
1229
1230 uint32_t key = 0;
1231 static_assert(SHIFT_LAYER_GROUP + BITS_FOR_LAYER_GROUP == sizeof(key) * 8, "Bit field too small");
1232
1233 key = (group_i << SHIFT_LAYER_GROUP) | (static_cast<uint32_t>(loc.y + MAX_BORDER) << SHIFT_Y);
1234 key |= (x_parity << SHIFT_X_PARITY);
1235 key |= (static_cast<uint32_t>(layer) << SHIFT_LAYER) | static_cast<uint32_t>(loc.x + MAX_BORDER) / 2;
1236
1237 return key;
1238}
1239} // namespace
1240
1242{
1243 drawing_buffer_.AGGREGATE_EMPLACE(generate_hex_key(layer, loc), std::move(draw_func), get_location_rect(loc));
1244}
1245
1247{
1248 DBG_DP << "committing drawing buffer"
1249 << " with " << drawing_buffer_.size() << " items";
1250
1251 // std::list::sort() is a stable sort
1252 drawing_buffer_.sort();
1253
1254 const auto clipper = draw::reduce_clip(map_area());
1255
1256 /*
1257 * Info regarding the rendering algorithm.
1258 *
1259 * In order to render a hex properly it needs to be rendered per row. On
1260 * this row several layers need to be drawn at the same time. Mainly the
1261 * unit and the background terrain. This is needed since both can spill
1262 * in the next hex. The foreground terrain needs to be drawn before to
1263 * avoid decapitation a unit.
1264 *
1265 * This ended in the following priority order:
1266 * layergroup > location > layer > 'draw_helper' > surface
1267 */
1268 for(const draw_helper& helper : drawing_buffer_) {
1269 std::invoke(helper.do_draw, helper.dest);
1270 }
1271
1272 drawing_buffer_.clear();
1273}
1274
1276{
1277 // Most panels are transparent.
1278 if (panel.image().empty()) {
1279 return;
1280 }
1281
1282 const rect& loc = panel.location(video::game_canvas());
1283
1284 if (!loc.overlaps(draw::get_clip())) {
1285 return;
1286 }
1287
1288 DBG_DP << "drawing panel " << panel.get_id() << ' ' << loc;
1289
1290 texture tex(image::get_texture(panel.image()));
1291 if (!tex) {
1292 ERR_DP << "failed to load panel " << panel.get_id()
1293 << " texture: " << panel.image();
1294 return;
1295 }
1296
1297 draw::tiled(tex, loc);
1298}
1299
1301{
1302 const rect& loc = label.location(video::game_canvas());
1303
1304 if (!loc.overlaps(draw::get_clip())) {
1305 return;
1306 }
1307
1308 const std::string& text = label.text();
1309 const color_t text_color = label.font_rgb_set() ? label.font_rgb() : font::NORMAL_COLOR;
1310 const std::string& icon = label.icon();
1311
1312 DBG_DP << "drawing label " << label.get_id() << ' ' << loc;
1313
1314 if(icon.empty() == false) {
1316
1317 if(text.empty() == false) {
1319 }
1320 } else if(text.empty() == false) {
1322 renderer.set_text(text, false);
1323 renderer.set_family_class(font::family_class::sans_serif);
1324 renderer.set_font_size(label.font_size());
1326 renderer.set_foreground_color(text_color);
1327 renderer.set_ellipse_mode(PANGO_ELLIPSIZE_END);
1328 renderer.set_maximum_width(loc.w);
1329 renderer.set_maximum_height(loc.h, true);
1330
1331 auto t = renderer.render_and_get_texture();
1332 draw::blit(t, rect{ loc.origin(), t.draw_size() });
1333 }
1334}
1335
1337{
1338 bool drew = false;
1339 const rect game_canvas = video::game_canvas();
1340
1341 for(const auto& panel : theme_.panels()) {
1342 if(region.overlaps(panel.location(game_canvas))) {
1343 draw_panel(panel);
1344 drew = true;
1345 }
1346 }
1347
1348 for(const auto& label : theme_.labels()) {
1349 if(region.overlaps(label.location(game_canvas))) {
1351 drew = true;
1352 }
1353 }
1354
1355 return drew;
1356}
1357
1359 const drawing_layer layer,
1360 const std::string& text,
1361 std::size_t font_size,
1362 color_t color,
1363 double x_in_hex,
1364 double y_in_hex)
1365{
1366 if (text.empty()) return;
1367
1369 renderer.set_text(text, false);
1370 renderer.set_font_size(font_size * get_zoom_factor());
1371 renderer.set_maximum_width(-1);
1372 renderer.set_maximum_height(-1, false);
1373 renderer.set_foreground_color(color);
1374 renderer.set_add_outline(true);
1375
1376 drawing_buffer_add(layer, loc, [x_in_hex, y_in_hex, tex = renderer.render_and_get_texture()](const rect& dest) {
1377 draw::blit(tex, rect{ dest.point_at(x_in_hex, y_in_hex) - tex.draw_size() / 2, tex.draw_size() });
1378 });
1379}
1380
1388
1390{
1391 if(mouseoverHex_ == hex) {
1392 return;
1393 }
1395 mouseoverHex_ = hex;
1397}
1398
1399void display::set_diagnostic(const std::string& msg)
1400{
1401 if(diagnostic_label_ != 0) {
1404 }
1405
1406 if(!msg.empty()) {
1407 font::floating_label flabel(msg);
1410 flabel.set_position(300, 50);
1412
1414 }
1415}
1416
1418{
1419 for(auto i = action_buttons_.begin(); i != action_buttons_.end(); ++i) {
1420 if((*i)->pressed()) {
1421 const std::size_t index = std::distance(action_buttons_.begin(), i);
1422 if(index >= theme_.actions().size()) {
1423 assert(false);
1424 return nullptr;
1425 }
1426 return &theme_.actions()[index];
1427 }
1428 }
1429
1430 return nullptr;
1431}
1432
1434{
1435 for(auto i = menu_buttons_.begin(); i != menu_buttons_.end(); ++i) {
1436 if((*i)->pressed()) {
1437 const std::size_t index = std::distance(menu_buttons_.begin(), i);
1438 if(index >= theme_.menus().size()) {
1439 assert(false);
1440 return nullptr;
1441 }
1442 return theme_.get_menu_item((*i)->id());
1443 }
1444 }
1445
1446 return nullptr;
1447}
1448
1449void display::announce(const std::string& message, const color_t& color, const announce_options& options)
1450{
1451 if(options.discard_previous) {
1452 font::remove_floating_label(prevLabel);
1453 }
1454 font::floating_label flabel(message);
1456 flabel.set_color(color);
1457 flabel.set_position(
1459 flabel.set_lifetime(options.lifetime);
1461
1462 prevLabel = font::add_floating_label(flabel);
1463}
1464
1466{
1467 if(video::headless()) {
1468 return;
1469 }
1470
1471 const rect& area = minimap_area();
1472 if(area.empty()){
1473 return;
1474 }
1475
1477 context().map(),
1478 context().teams().empty() ? nullptr : &viewing_team(),
1479 &context().units(),
1480 (selectedHex_.valid() && !is_blindfolded()) ? &reach_map_ : nullptr
1481 );
1482
1484}
1485
1490
1492{
1493 const rect& area = minimap_area();
1494
1495 if(area.empty() || !area.overlaps(draw::get_clip())) {
1496 return;
1497 }
1498
1499 if(!minimap_renderer_) {
1500 return;
1501 }
1502
1503 const auto clipper = draw::reduce_clip(area);
1504
1505 // Draw the minimap background.
1506 draw::fill(area, 31, 31, 23);
1507
1508 // Draw the minimap and update its location for mouse and units functions
1509 minimap_location_ = std::invoke(minimap_renderer_, area);
1510
1511 // calculate the visible portion of the map:
1512 // scaling between minimap and full map images
1513 double xscaling = 1.0 * minimap_location_.w / (context().map().w() * hex_width());
1514 double yscaling = 1.0 * minimap_location_.h / (context().map().h() * hex_size());
1515
1516 // we need to shift with the border size
1517 // and the 0.25 from the minimap balanced drawing
1518 // and the possible difference between real map and outside off-map
1519 rect map_rect = map_area();
1520 rect map_out_rect = map_outside_area();
1521 double border = theme_.border().size;
1522 double shift_x = -border * hex_width() - (map_out_rect.w - map_rect.w) / 2;
1523 double shift_y = -(border + 0.25) * hex_size() - (map_out_rect.h - map_rect.h) / 2;
1524
1525 int view_x = static_cast<int>((viewport_origin_.x + shift_x) * xscaling);
1526 int view_y = static_cast<int>((viewport_origin_.y + shift_y) * yscaling);
1527 int view_w = static_cast<int>(map_out_rect.w * xscaling);
1528 int view_h = static_cast<int>(map_out_rect.h * yscaling);
1529
1530 rect outline_rect {
1531 minimap_location_.x + view_x - 1,
1532 minimap_location_.y + view_y - 1,
1533 view_w + 2,
1534 view_h + 2
1535 };
1536
1537 draw::rect(outline_rect, 255, 255, 255);
1538}
1539
1540bool display::scroll(const point& amount, bool force)
1541{
1542 if(view_locked_ && !force) {
1543 return false;
1544 }
1545
1546 // No move offset, do nothing.
1547 if(amount == point{}) {
1548 return false;
1549 }
1550
1551 point new_pos = viewport_origin_ + amount;
1552 bounds_check_position(new_pos.x, new_pos.y);
1553
1554 // Camera position doesn't change, exit.
1555 if(viewport_origin_ == new_pos) {
1556 return false;
1557 }
1558
1559 point diff = viewport_origin_ - new_pos;
1560 viewport_origin_ = new_pos;
1561
1562 /* Adjust floating label positions. This only affects labels whose position is anchored
1563 * to the map instead of the screen. In order to do that, we want to adjust their drawing
1564 * coordinates in the opposite direction of the screen scroll.
1565 *
1566 * The check a few lines up prevents any scrolling from happening if the camera position
1567 * doesn't change. Without that, the label still scroll even when the map edge is reached.
1568 * If that's removed, the following formula should work instead:
1569 *
1570 * const int label_[x,y]_adjust = [x,y]pos_ - new_[x,y];
1571 */
1572 font::scroll_floating_labels(diff.x, diff.y);
1573
1575
1576 //
1577 // NOTE: the next three blocks can be removed once we switch to accelerated rendering.
1578 //
1579
1580 if(!video::headless()) {
1581 rect dst = map_area();
1582 dst.shift(diff);
1583 dst.clip(map_area());
1584
1585 rect src = dst;
1586 src.shift(-diff);
1587
1588 // swap buffers
1589 std::swap(front_, back_);
1590
1591 // Set the source region to blit from
1592 back_.set_src(src);
1593
1594 // copy from the back to the front buffer
1595 auto rts = draw::set_render_target(front_);
1597
1598 back_.clear_src();
1599
1600 // queue repaint
1602 }
1603
1604 if(diff.y != 0) {
1605 rect r = map_area();
1606
1607 if(diff.y < 0) {
1608 r.y = r.y + r.h + diff.y;
1609 }
1610
1611 r.h = std::abs(diff.y);
1613 }
1614
1615 if(diff.x != 0) {
1616 rect r = map_area();
1617
1618 if(diff.x < 0) {
1619 r.x = r.x + r.w + diff.x;
1620 }
1621
1622 r.w = std::abs(diff.x);
1624 }
1625
1627
1629
1630 return true;
1631}
1632
1634{
1635 return zoom_ == MaxZoom;
1636}
1637
1639{
1640 return zoom_ == MinZoom;
1641}
1642
1643bool display::set_zoom(bool increase)
1644{
1645 // Ensure we don't try to access nonexistent vector indices.
1646 zoom_index_ = std::clamp(increase ? zoom_index_ + 1 : zoom_index_ - 1, 0, final_zoom_index);
1647
1648 // No validation check is needed in the next step since we've already set the index here and
1649 // know the new zoom value is indeed valid.
1650 return set_zoom(zoom_levels[zoom_index_], false);
1651}
1652
1653bool display::set_zoom(unsigned int amount, const bool validate_value_and_set_index)
1654{
1655 unsigned int new_zoom = std::clamp(amount, MinZoom, MaxZoom);
1656
1657 LOG_DP << "new_zoom = " << new_zoom;
1658
1659 if(new_zoom == zoom_) {
1660 return false;
1661 }
1662
1663 if(validate_value_and_set_index) {
1665 new_zoom = zoom_levels[zoom_index_];
1666 }
1667
1668 if((new_zoom / 4) * 4 != new_zoom) {
1669 WRN_DP << "set_zoom forcing zoom " << new_zoom
1670 << " which is not a multiple of 4."
1671 << " This will likely cause graphical glitches.";
1672 }
1673
1675 const rect area = map_area();
1676
1677 // Turn the zoom factor to a double in order to avoid rounding errors.
1678 double zoom_factor = static_cast<double>(new_zoom) / static_cast<double>(zoom_);
1679
1680 // INVARIANT: xpos_ + area.w == xend where xend is as in bounds_check_position()
1681 //
1682 // xpos_: Position of the leftmost visible map pixel of the viewport, in pixels.
1683 // Affected by the current zoom: this->zoom_ pixels to the hex.
1684 //
1685 // xpos_ + area.w/2: Position of the center of the viewport, in pixels.
1686 //
1687 // (xpos_ + area.w/2) * new_zoom/zoom_: Position of the center of the
1688 // viewport, as it would be under new_zoom.
1689 //
1690 // (xpos_ + area.w/2) * new_zoom/zoom_ - area.w/2: Position of the
1691 // leftmost visible map pixel, as it would be under new_zoom.
1692 viewport_origin_.x = std::round(((viewport_origin_.x + area.w / 2) * zoom_factor) - (area.w / 2));
1693 viewport_origin_.y = std::round(((viewport_origin_.y + area.h / 2) * zoom_factor) - (area.h / 2));
1694 viewport_origin_ -= (outside_area.size() - area.size()) / 2;
1695
1696 zoom_ = new_zoom;
1698 if(zoom_ != DefaultZoom) {
1699 last_zoom_ = zoom_;
1700 }
1701
1702 prefs::get().set_tile_size(zoom_);
1703
1705 redraw_background_ = true;
1707
1708 return true;
1709}
1710
1712{
1713 if (zoom_ != DefaultZoom) {
1714 last_zoom_ = zoom_;
1716 } else {
1717 // When we are already at the default zoom,
1718 // switch to the last zoom used
1720 }
1721}
1722
1727
1729{
1730 const auto [x, y] = get_location(loc);
1731 const rect area = map_area();
1732 int hw = hex_width(), hs = hex_size();
1733 return x + hs >= area.x - hw && x < area.x + area.w + hw &&
1734 y + hs >= area.y - hs && y < area.y + area.h + hs;
1735}
1736
1737void display::scroll_to_xy(const point& screen_coordinates, SCROLL_TYPE scroll_type, bool force)
1738{
1739 if(!force && (view_locked_ || !prefs::get().scroll_to_action())) return;
1740 if(video::headless()) {
1741 return;
1742 }
1743
1744 point expected_move = screen_coordinates - map_area().center();
1745
1746 point new_pos = viewport_origin_ + expected_move;
1747 bounds_check_position(new_pos.x, new_pos.y);
1748
1749 point move = new_pos - viewport_origin_;
1750
1751 if(scroll_type == WARP || scroll_type == ONSCREEN_WARP || turbo_speed() > 2.0 || prefs::get().scroll_speed() > 99) {
1752 scroll(move, true);
1754 events::draw();
1755 return;
1756 }
1757
1758 // Doing an animated scroll, with acceleration etc.
1759
1760 point prev_pos;
1761 const double dist_total = std::hypot(move.x, move.y);
1762 double dist_moved = 0.0;
1763
1764 using fractional_seconds = std::chrono::duration<double>;
1765 auto prev_time = std::chrono::steady_clock::now();
1766
1767 double velocity = 0.0;
1768 while (dist_moved < dist_total) {
1769 events::pump();
1770
1771 auto time = std::chrono::steady_clock::now();
1772 auto dt = fractional_seconds{time - prev_time};
1773
1774 // Do not skip too many frames on slow PCs
1775 dt = std::min<fractional_seconds>(dt, 200ms);
1776 prev_time = time;
1777
1778 const double dt_as_double = dt.count();
1779 const double accel_time = 0.3 / turbo_speed(); // seconds until full speed is reached
1780 const double decel_time = 0.4 / turbo_speed(); // seconds from full speed to stop
1781
1782 double velocity_max = prefs::get().scroll_speed() * 60.0;
1783 velocity_max *= turbo_speed();
1784 double accel = velocity_max / accel_time;
1785 double decel = velocity_max / decel_time;
1786
1787 // If we started to decelerate now, where would we stop?
1788 double stop_time = velocity / decel;
1789 double dist_stop = dist_moved + velocity*stop_time - 0.5*decel*stop_time*stop_time;
1790 if (dist_stop > dist_total || velocity > velocity_max) {
1791 velocity -= decel * dt_as_double;
1792 if (velocity < 1.0) velocity = 1.0;
1793 } else {
1794 velocity += accel * dt_as_double;
1795 if (velocity > velocity_max) velocity = velocity_max;
1796 }
1797
1798 dist_moved += velocity * dt_as_double;
1799 if (dist_moved > dist_total) dist_moved = dist_total;
1800
1801 point next_pos(
1802 std::round(move.x * dist_moved / dist_total),
1803 std::round(move.y * dist_moved / dist_total)
1804 );
1805
1806 point diff = next_pos - prev_pos;
1807 scroll(diff, true);
1808 prev_pos += diff;
1809
1811 events::draw();
1812 }
1813}
1814
1815void display::scroll_to_tile(const map_location& loc, SCROLL_TYPE scroll_type, bool check_fogged, bool force)
1816{
1817 if(context().map().on_board(loc) == false) {
1818 ERR_DP << "Tile at " << loc << " isn't on the map, can't scroll to the tile.";
1819 return;
1820 }
1821
1822 scroll_to_tiles({loc}, scroll_type, check_fogged, false, 0.0, force);
1823}
1824
1826 SCROLL_TYPE scroll_type, bool check_fogged,
1827 double add_spacing, bool force)
1828{
1829 scroll_to_tiles({loc1, loc2}, scroll_type, check_fogged, false, add_spacing, force);
1830}
1831
1832void display::scroll_to_tiles(const std::vector<map_location>& locs,
1833 SCROLL_TYPE scroll_type, bool check_fogged,
1834 bool only_if_possible, double add_spacing, bool force)
1835{
1836 // basically we calculate the min/max coordinates we want to have on-screen
1837 int minx = 0;
1838 int maxx = 0;
1839 int miny = 0;
1840 int maxy = 0;
1841 bool valid = false;
1842
1843 for(const map_location& loc : locs) {
1844 if(context().map().on_board(loc) == false) continue;
1845 if(check_fogged && fogged(loc)) continue;
1846
1847 const auto [x, y] = get_location(loc);
1848
1849 if (!valid) {
1850 minx = x;
1851 maxx = x;
1852 miny = y;
1853 maxy = y;
1854 valid = true;
1855 } else {
1856 int minx_new = std::min<int>(minx,x);
1857 int miny_new = std::min<int>(miny,y);
1858 int maxx_new = std::max<int>(maxx,x);
1859 int maxy_new = std::max<int>(maxy,y);
1860 rect r = map_area();
1861 r.x = minx_new;
1862 r.y = miny_new;
1863 if(outside_area(r, maxx_new, maxy_new)) {
1864 // we cannot fit all locations to the screen
1865 if (only_if_possible) return;
1866 break;
1867 }
1868 minx = minx_new;
1869 miny = miny_new;
1870 maxx = maxx_new;
1871 maxy = maxy_new;
1872 }
1873 }
1874 //if everything is fogged or the location list is empty
1875 if(!valid) return;
1876
1877 if (scroll_type == ONSCREEN || scroll_type == ONSCREEN_WARP) {
1878 int spacing = std::round(add_spacing * hex_size());
1879 rect r = map_area().padded_by(-spacing); // Shrink
1880 if (!outside_area(r, minx,miny) && !outside_area(r, maxx,maxy)) {
1881 return;
1882 }
1883 }
1884
1885 // let's do "normal" rectangle math from now on
1886 rect locs_bbox;
1887 locs_bbox.x = minx;
1888 locs_bbox.y = miny;
1889 locs_bbox.w = maxx - minx + hex_size();
1890 locs_bbox.h = maxy - miny + hex_size();
1891
1892 // target the center
1893 point target = locs_bbox.center();
1894
1895 if (scroll_type == ONSCREEN || scroll_type == ONSCREEN_WARP) {
1896 // when doing an ONSCREEN scroll we do not center the target unless needed
1897 rect r = map_area();
1898 auto [map_center_x, map_center_y] = r.center();
1899
1900 int h = r.h;
1901 int w = r.w;
1902
1903 // we do not want to be only inside the screen rect, but center a bit more
1904 double inside_frac = 0.5; // 0.0 = always center the target, 1.0 = scroll the minimum distance
1905 w = static_cast<int>(w * inside_frac);
1906 h = static_cast<int>(h * inside_frac);
1907
1908 // shrink the rectangle by the size of the locations rectangle we found
1909 // such that the new task to fit a point into a rectangle instead of rectangle into rectangle
1910 w -= locs_bbox.w;
1911 h -= locs_bbox.h;
1912
1913 if (w < 1) w = 1;
1914 if (h < 1) h = 1;
1915
1916 r.x = target.x - w/2;
1917 r.y = target.y - h/2;
1918 r.w = w;
1919 r.h = h;
1920
1921 // now any point within r is a possible target to scroll to
1922 // we take the one with the minimum distance to map_center
1923 // which will always be at the border of r
1924
1925 if (map_center_x < r.x) {
1926 target.x = r.x;
1927 target.y = std::clamp(map_center_y, r.y, r.y + r.h - 1);
1928 } else if (map_center_x > r.x+r.w-1) {
1929 target.x = r.x + r.w - 1;
1930 target.y = std::clamp(map_center_y, r.y, r.y + r.h - 1);
1931 } else if (map_center_y < r.y) {
1932 target.y = r.y;
1933 target.x = std::clamp(map_center_x, r.x, r.x + r.w - 1);
1934 } else if (map_center_y > r.y+r.h-1) {
1935 target.y = r.y + r.h - 1;
1936 target.x = std::clamp(map_center_x, r.x, r.x + r.w - 1);
1937 } else {
1938 ERR_DP << "Bug in the scrolling code? Looks like we would not need to scroll after all...";
1939 // keep the target at the center
1940 }
1941 }
1942
1943 scroll_to_xy(target, scroll_type, force);
1944}
1945
1946
1952
1953void display::bounds_check_position(int& xpos, int& ypos) const
1954{
1955 const int tile_width = hex_width();
1956
1957 // Adjust for the border 2 times
1958 const int xend = static_cast<int>(tile_width * (context().map().w() + 2 * theme_.border().size) + tile_width / 3);
1959 const int yend = static_cast<int>(zoom_ * (context().map().h() + 2 * theme_.border().size) + zoom_ / 2);
1960
1961 xpos = std::clamp(xpos, 0, xend - map_area().w);
1962 ypos = std::clamp(ypos, 0, yend - map_area().h);
1963}
1964
1966{
1967 bool res = prefs::get().turbo();
1968 if(keys_[SDLK_LSHIFT] || keys_[SDLK_RSHIFT]) {
1969 res = !res;
1970 }
1971
1972 res |= video::headless();
1973 if(res)
1974 return prefs::get().turbo_speed();
1975 else
1976 return 1.0;
1977}
1978
1980{
1981 prevent_draw_ = pd;
1982 if (!pd) {
1983 // ensure buttons are visible
1985 }
1986}
1987
1989{
1990 return prevent_draw_;
1991}
1992
1993submerge_data display::get_submerge_data(const rect& dest, double submerge, const point& size, uint8_t alpha, bool hreverse, bool vreverse)
1994{
1996 if(submerge <= 0.0) {
1997 return data;
1998 }
1999
2000 // Set up blit destinations
2001 data.unsub_dest = dest;
2002 const int dest_sub_h = dest.h * submerge;
2003 data.unsub_dest.h -= dest_sub_h;
2004 const int dest_y_mid = dest.y + data.unsub_dest.h;
2005
2006 // Set up blit src regions
2007 const int submersion_line = size.y * (1.0 - submerge);
2008 data.unsub_src = {0, 0, size.x, submersion_line};
2009
2010 // Set up shader vertices
2011 // alpha comes in as 0-255 but with SDL3 it's a float, so need to convert it
2012 float mid_alpha = 0.3 * (alpha/ALPHA_OPAQUE);
2013 const SDL_FColor c_mid{1.0, 1.0, 1.0, mid_alpha};
2014 const int pixels_submerged = size.y * submerge;
2015 float bot_alpha = 1.0;
2016 // be more transparent the more pixels are underwater
2017 bot_alpha -= (pixels_submerged * 0.035) * (alpha/ALPHA_OPAQUE);
2018
2019 // fully transparent seems to be -1.0 instead of 0.0 for some reason, so make sure it doesn't end up below -1.0
2020 const SDL_FColor c_bot{1.0, 1.0, 1.0, std::max(bot_alpha, -1.0f)};
2021 const SDL_FPoint pML{float(dest.x), float(dest_y_mid)};
2022 const SDL_FPoint pMR{float(dest.x + dest.w), float(dest_y_mid)};
2023 const SDL_FPoint pBL{float(dest.x), float(dest.y + dest.h)};
2024 const SDL_FPoint pBR{float(dest.x + dest.w), float(dest.y + dest.h)};
2025 data.alpha_verts = {
2026 SDL_Vertex{pML, c_mid, {0.0, float(1.0 - submerge)}},
2027 SDL_Vertex{pMR, c_mid, {1.0, float(1.0 - submerge)}},
2028 SDL_Vertex{pBL, c_bot, {0.0, 1.0}},
2029 SDL_Vertex{pBR, c_bot, {1.0, 1.0}},
2030 };
2031
2032 if(hreverse) {
2033 for(SDL_Vertex& v : data.alpha_verts) {
2034 v.tex_coord.x = 1.0 - v.tex_coord.x;
2035 }
2036 }
2037 if(vreverse) {
2038 for(SDL_Vertex& v : data.alpha_verts) {
2039 v.tex_coord.y = 1.0 - v.tex_coord.y;
2040 }
2041 }
2042
2043 return data;
2044}
2045
2047 const std::string& old_mask,
2048 const std::string& new_mask)
2049{
2050 // TODO: hwaccel - this needs testing as it's not used in mainline
2053
2054 auto duration = 300ms / turbo_speed();
2055 auto start = std::chrono::steady_clock::now();
2056 for(auto now = start; now < start + duration; now = std::chrono::steady_clock::now()) {
2057 uint8_t p = float_to_color(chrono::normalize_progress(now - start, duration));
2058 tod_hex_alpha2 = p;
2059 tod_hex_alpha1 = ~p;
2062 }
2063
2066}
2067
2068void display::fade_to(const color_t& c, const std::chrono::milliseconds& duration)
2069{
2070 auto start = std::chrono::steady_clock::now();
2071 color_t fade_start = fade_color_;
2072 color_t fade_end = c;
2073
2074 // If we started transparent, assume the same colour
2075 if(fade_start.a == 0) {
2076 fade_start.r = fade_end.r;
2077 fade_start.g = fade_end.g;
2078 fade_start.b = fade_end.b;
2079 }
2080
2081 // If we are ending transparent, assume the same colour
2082 if(fade_end.a == 0) {
2083 fade_end.r = fade_start.r;
2084 fade_end.g = fade_start.g;
2085 fade_end.b = fade_start.b;
2086 }
2087
2088 // Smoothly blend and display
2089 for(auto now = start; now < start + duration; now = std::chrono::steady_clock::now()) {
2090 uint8_t p = float_to_color(chrono::normalize_progress(now - start, duration));
2091 fade_color_ = fade_start.smooth_blend(fade_end, p);
2094 }
2095 fade_color_ = fade_end;
2097 events::draw();
2098}
2099
2101{
2102 fade_color_ = c;
2103}
2104
2106{
2107 if(video::headless())
2108 return;
2109
2110 DBG_DP << "redrawing everything";
2111
2112 // This is specifically for game_display.
2113 // It would probably be better to simply make this function virtual,
2114 // if game_display needs to do special processing.
2115 invalidateGameStatus_ = true;
2116
2117 reportLocations_.clear();
2118 reportSurfaces_.clear();
2119 reports_.clear();
2120
2122
2124
2126
2127 if(!menu_buttons_.empty() || !action_buttons_.empty()) {
2129 }
2130
2133 if(command_executor != nullptr) {
2134 // This function adds button overlays,
2135 // it needs to be run after recreating the buttons.
2136 command_executor->set_button_state();
2137 }
2138 }
2139
2140 if(!gui2::is_in_dialog()) {
2142 }
2143
2144 redraw_background_ = true;
2145
2146 // This is only for one specific use, which is by the editor controller.
2147 // It would be vastly better if this didn't exist.
2148 for(std::function<void(display&)> f : redraw_observers_) {
2149 f(*this);
2150 }
2151
2153
2155}
2156
2158{
2159 // Could redraw a smaller region if the display doesn't use it all,
2160 // but when does that ever happen?
2162}
2163
2164void display::add_redraw_observer(const std::function<void(display&)>& f)
2165{
2166 redraw_observers_.push_back(f);
2167}
2168
2173
2175{
2176 if(video::headless()) {
2177 DBG_DP << "display::draw denied";
2178 return;
2179 }
2180 //DBG_DP << "display::draw";
2181
2182 // I have no idea why this is messing with sync context,
2183 // but i'm not going to touch it.
2185
2186 // This isn't the best, but also isn't important enough to do better.
2188 DBG_DP << "display::draw redraw background";
2191 redraw_background_ = false;
2192 }
2193
2194 if(!context().map().empty()) {
2195 if(!invalidated_.empty()) {
2197 invalidated_.clear();
2198 }
2200 }
2201}
2202
2204{
2205 //DBG_DP << "display::update";
2206 // Ensure render textures are correctly sized and up-to-date.
2208
2209 // Trigger cache rebuild if animated water preference has changed.
2210 if(animate_water_ != prefs::get().animate_water()) {
2211 animate_water_ = prefs::get().animate_water();
2212 builder_->rebuild_cache_all();
2213 }
2214
2217 }
2218}
2219
2221{
2222 //DBG_DP << "display::layout";
2223
2224 // There's nothing that actually does layout here, it all happens in
2225 // response to events. This isn't ideal, but neither is changing that.
2226
2227 // Post-layout / Pre-render
2228
2229 if (!context().map().empty()) {
2230 if(redraw_background_) {
2231 invalidateAll_ = true;
2232 }
2233 if(invalidateAll_) {
2234 DBG_DP << "draw() with invalidateAll";
2235
2236 // toggle invalidateAll_ first to allow regular invalidations
2237 invalidateAll_ = false;
2239
2241 }
2242 }
2243
2244 // invalidate animated terrain, units and haloes
2246
2247 // Update and invalidate floating labels as necessary
2249}
2250
2252{
2253 // This should render the game map and units.
2254 // It is not responsible for halos and floating labels.
2255 //DBG_DP << "display::render";
2256
2257 // No need to render if we aren't going to draw anything.
2258 if(prevent_draw_) {
2259 DBG_DP << "render prevented";
2260 return;
2261 }
2262
2263 // Update our frametime values
2264 tracked_drawable::update_count();
2265
2266 // render to the offscreen buffer
2267 auto target_setter = draw::set_render_target(front_);
2268 draw();
2269
2270 // update the minimap texture, if necessary
2271 // TODO: highdpi - high DPI minimap
2272 const rect& area = minimap_area();
2273 if(!area.empty() && !minimap_renderer_) {
2275 }
2276}
2277
2278bool display::expose(const rect& region)
2279{
2280 if(prevent_draw_) {
2281 DBG_DP << "draw prevented";
2282 return false;
2283 }
2284
2285 rect clipped_region = draw::get_clip().intersect(region);
2286
2287 // Blit from the pre-rendered front buffer.
2288 if(clipped_region.overlaps(map_outside_area())) {
2289 front_.set_src(clipped_region);
2290 draw::blit(front_, clipped_region);
2291 front_.clear_src();
2292 }
2293
2294 // Render halos.
2295 halo_man_.render(clipped_region);
2296
2297 // Render UI elements.
2298 // Ideally buttons would be drawn as part of panels,
2299 // but they are currently TLDs so they draw themselves.
2300 // This also means they draw over tooltips...
2301 draw_all_panels(clipped_region);
2302 draw_reports(clipped_region);
2303 if(clipped_region.overlaps(minimap_area())) {
2304 draw_minimap();
2305 }
2306
2307 // Floating labels should probably be separated by type,
2308 // but they aren't so they all get drawn here.
2310
2311 // If there's a fade, apply it over everything
2312 if(fade_color_.a) {
2313 draw::fill(map_outside_area().intersect(region), fade_color_);
2314 }
2315
2316 DBG_DP << "display::expose " << region;
2317
2318 // The display covers the entire screen.
2319 // We will always be drawing something.
2320 return true;
2321}
2322
2324{
2325 assert(!map_screenshot_);
2326 // There's no good way to determine this, as themes can put things
2327 // anywhere. Just return the entire game canvas.
2328 return video::game_canvas();
2329}
2330
2332{
2333 if(video::headless()) {
2334 return;
2335 }
2336
2337 // We ignore any logical offset on the underlying window buffer.
2338 // Render buffer size is always a simple multiple of the draw area.
2339 rect darea = video::game_canvas();
2340 rect oarea = darea * video::get_pixel_scale();
2341
2342 // Check that the front buffer size is correct.
2343 // Buffers are always resized together, so we only need to check one.
2344 point size = front_.get_raw_size();
2345 point dsize = front_.draw_size();
2346 bool raw_size_changed = size.x != oarea.w || size.y != oarea.h;
2347 bool draw_size_changed = dsize.x != darea.w || dsize.y != darea.h;
2348 if (!raw_size_changed && !draw_size_changed) {
2349 // buffers are fine
2350 return;
2351 }
2352
2353 if(raw_size_changed) {
2354 LOG_DP << "regenerating render buffers as " << oarea;
2355 front_ = texture(oarea.w, oarea.h, SDL_TEXTUREACCESS_TARGET);
2356 back_ = texture(oarea.w, oarea.h, SDL_TEXTUREACCESS_TARGET);
2357 }
2358 if(raw_size_changed || draw_size_changed) {
2359 LOG_DP << "updating render buffer draw size to " << darea;
2360 front_.set_draw_size(darea.w, darea.h);
2361 back_.set_draw_size(darea.w, darea.h);
2362 }
2363
2364 // Fill entire texture with black, just in case
2365 for(int i = 0; i < 2; ++i) {
2367 draw::fill(0,0,0);
2368 }
2369
2370 // Fill in the background area on both textures.
2372
2374}
2375
2377{
2378 // This could be optimized to avoid the map area,
2379 // but it's only called on game creation or zoom anyway.
2380 const rect clip_rect = map_outside_area();
2382 for(int i = 0; i < 2; ++i) {
2384 if(bgtex) {
2385 draw::tiled(bgtex, clip_rect);
2386 } else {
2387 draw::fill(clip_rect, 0, 0, 0);
2388 }
2389 }
2390}
2391
2393{
2394 return *map_labels_;
2395}
2396
2398{
2399 return *map_labels_;
2400}
2401
2403{
2404 return map_area();
2405}
2406
2408{
2409 // log_scope("display::draw_invalidated");
2410 rect clip_rect = get_clip_rect();
2411 const auto clipper = draw::reduce_clip(clip_rect);
2412
2413 DBG_DP << "drawing " << invalidated_.size() << " invalidated hexes with clip " << clip_rect;
2414
2415 // The unit drawer can't function without teams
2416 utils::optional<unit_drawer> drawer{};
2417 if(!context().teams().empty()) {
2418 drawer.emplace(*this);
2419 }
2420
2421 for(const map_location& loc : invalidated_) {
2422 rect hex_rect = get_location_rect(loc);
2423 if(!hex_rect.overlaps(clip_rect)) {
2424 continue;
2425 }
2426
2427 draw_hex(loc);
2428 drawn_hexes_ += 1;
2429
2430 if(drawer) {
2431 const auto u_it = context().units().find(loc);
2432 if(u_it != context().units().end() && unit_can_draw_here(loc, *u_it)) {
2433 drawer->redraw_unit(*u_it);
2434 }
2435 }
2436
2437 draw_manager::invalidate_region(hex_rect.intersect(clip_rect));
2438 }
2439
2441}
2442
2444{
2445 const bool on_map = context().map().on_board(loc);
2446 const time_of_day& tod = get_time_of_day(loc);
2447
2448 int num_images_fg = 0;
2449 int num_images_bg = 0;
2450
2451 const bool is_shrouded = shrouded(loc);
2452
2453 // unshrouded terrain (the normal case)
2454 if(!is_shrouded) {
2455 get_terrain_images(loc, tod.id, BACKGROUND); // updates terrain_image_vector_
2456 num_images_bg = terrain_image_vector_.size();
2457
2458 drawing_buffer_add(drawing_layer::terrain_bg, loc, [images = std::exchange(terrain_image_vector_, {})](const rect& dest) {
2459 for(const texture& t : images) {
2460 draw::blit(t, dest);
2461 }
2462 });
2463
2464 get_terrain_images(loc, tod.id, FOREGROUND); // updates terrain_image_vector_
2465 num_images_fg = terrain_image_vector_.size();
2466
2467 drawing_buffer_add(drawing_layer::terrain_fg, loc, [images = std::exchange(terrain_image_vector_, {})](const rect& dest) {
2468 for(const texture& t : images) {
2469 draw::blit(t, dest);
2470 }
2471 });
2472
2473 // Draw the grid, if that's been enabled
2474 if(prefs::get().grid()) {
2477
2479 [tex = image::get_texture(grid_top, image::TOD_COLORED)](const rect& dest) { draw::blit(tex, dest); });
2480
2482 [tex = image::get_texture(grid_bottom, image::TOD_COLORED)](const rect& dest) { draw::blit(tex, dest); });
2483 }
2484
2485 // overlays (TODO: can we just draw all the overlays in one pass instead of per-hex?)
2487
2488 // village-control flags.
2489 if(context().map().is_village(loc)) {
2491 [tex = get_flag(loc)](const rect& dest) { draw::blit(tex, dest); });
2492 }
2493 }
2494
2495 // Draw the time-of-day mask on top of the terrain in the hex.
2496 // tod may differ from tod if hex is illuminated.
2497 const std::string& tod_hex_mask = tod.image_mask;
2499 drawing_buffer_add(drawing_layer::terrain_fg, loc, [this](const rect& dest) mutable {
2502
2505 });
2506 } else if(!tod_hex_mask.empty()) {
2508 [tex = image::get_texture(tod_hex_mask, image::HEXED)](const rect& dest) { draw::blit(tex, dest); });
2509 }
2510
2511 // Paint arrows
2512 if(auto arrows_in_hex = arrows_map_.find(loc); arrows_in_hex != arrows_map_.end()) {
2513 std::vector<texture> to_draw;
2514 for(const arrow* a : arrows_in_hex->second) {
2515 to_draw.push_back(image::get_texture(a->get_image_for_loc(loc)));
2516 }
2517
2518 drawing_buffer_add(drawing_layer::arrows, loc, [to_draw = std::move(to_draw)](const rect& dest) {
2519 for(const texture& t : to_draw) {
2520 draw::blit(t, dest);
2521 }
2522 });
2523 }
2524
2525 // Apply shroud, fog and linger overlay
2526
2527 if(is_shrouded || fogged(loc)) {
2528 // TODO: better noise function
2529 const auto get_variant = [&loc](const std::vector<std::string>& variants) -> const auto& {
2530 return variants[std::abs(loc.x + loc.y) % variants.size()];
2531 };
2532
2533 const std::string& img = get_variant(is_shrouded ? shroud_images_ : fog_images_);
2535 [tex = image::get_texture(img, image::TOD_COLORED)](const rect& dest) { draw::blit(tex, dest); });
2536 }
2537
2538 if(!is_shrouded) {
2540 for(const texture& t : images) {
2541 draw::blit(t, dest);
2542 }
2543 });
2544 }
2545
2547 using namespace std::string_literals;
2549 [tex = image::get_texture("terrain/foreground.png"s)](const rect& dest) { draw::blit(tex, dest); });
2550 }
2551
2552 if(on_map) {
2553 // This might be slight overkill. Basically, we want to check that none of the
2554 // first three bits in the debug flag bitset are set so we can avoid creating
2555 // a stringstream, a temp string, and attempting to trim it for every hex even
2556 // when none of these flags are set. This gives us a temp object with all bits
2557 // past the first three zeroed out.
2558 if((std::as_const(debug_flags_) << (NUM_DEBUG_FLAGS - DEBUG_FOREGROUND)).none()) {
2559 return;
2560 }
2561
2562 std::ostringstream ss;
2564 ss << loc << '\n';
2565 }
2566
2568 ss << context().map().get_terrain(loc) << '\n';
2569 }
2570
2572 ss << (num_images_bg + num_images_fg) << '\n';
2573 }
2574
2575 std::string output = ss.str();
2576 boost::trim(output);
2577
2578 if(output.empty()) {
2579 return;
2580 }
2581
2583 renderer.set_text(output, false);
2584 renderer.set_font_size(font::SIZE_TINY);
2585 renderer.set_alignment(PANGO_ALIGN_CENTER);
2586 renderer.set_foreground_color(font::NORMAL_COLOR);
2587 renderer.set_maximum_height(-1, false);
2588 renderer.set_maximum_width(-1);
2589
2590 drawing_buffer_add(drawing_layer::fog_shroud, loc, [tex = renderer.render_and_get_texture()](const rect& dest) {
2591 // Center text in dest rect
2592 const rect text_dest { dest.center() - tex.draw_size() / 2, tex.draw_size() };
2593
2594 // Add a little padding to the bg
2595 const rect bg_dest = text_dest.padded_by(3);
2596
2597 draw::fill(bg_dest, 0, 0, 0, 170);
2598 draw::blit(tex, text_dest);
2599 });
2600 }
2601}
2602
2604{
2605 auto it = get_overlays().find(loc);
2606 if(it == get_overlays().end()) {
2607 return;
2608 }
2609
2610 std::vector<overlay>& overlays = it->second;
2611 if(overlays.empty()) {
2612 return;
2613 }
2614
2615 const time_of_day& tod = get_time_of_day(loc);
2616 tod_color tod_col = tod.color + color_adjust_;
2617
2618 std::vector lt{image::light_adjust{-1, tod_col.r, tod_col.g, tod_col.b}};
2619
2620 for(const overlay& ov : overlays) {
2621 if(fogged(loc) && !ov.visible_in_fog) {
2622 continue;
2623 }
2624
2625 if(dont_show_all_ && !ov.team_name.empty()) {
2626 const auto current_team_names = utils::split_view(viewing_team().team_name());
2627 const auto team_names = utils::split_view(ov.team_name);
2628
2629 bool item_visible_for_team = std::find_first_of(team_names.begin(), team_names.end(),
2630 current_team_names.begin(), current_team_names.end()) != team_names.end();
2631
2632 if(!item_visible_for_team) {
2633 continue;
2634 }
2635 }
2636
2637 texture tex = ov.image.find("~NO_TOD_SHIFT()") == std::string::npos
2638 ? image::get_lighted_texture(ov.image, lt)
2639 : image::get_texture(ov.image, image::HEXED);
2640
2641 // Base submerge value for the terrain at this location
2642 const double ter_sub = context().map().get_terrain_info(loc).unit_submerge();
2643
2645 drawing_layer::terrain_bg, loc, [tex, ter_sub, ovr_sub = ov.submerge](const rect& dest) mutable {
2646 if(ovr_sub > 0.0 && ter_sub > 0.0) {
2647 // Adjust submerge appropriately
2648 double submerge = ter_sub * ovr_sub;
2649
2650 submerge_data data
2651 = display::get_submerge_data(dest, submerge, tex.draw_size(), ALPHA_OPAQUE, false, false);
2652
2653 // set clip for dry part
2654 // smooth_shaded doesn't use the clip information so it's fine to set it up front
2655 // TODO: do we need to unset this?
2656 tex.set_src(data.unsub_src);
2657
2658 // draw underwater part
2659 draw::smooth_shaded(tex, data.alpha_verts);
2660
2661 // draw dry part
2662 draw::blit(tex, data.unsub_dest);
2663 } else {
2664 // draw whole texture
2665 draw::blit(tex, dest);
2666 }
2667 });
2668 }
2669}
2670
2671/**
2672 * Redraws the specified report (if anything has changed).
2673 * If a config is not supplied, it will be generated via
2674 * reports::generate_report().
2675 */
2676void display::refresh_report(const std::string& report_name, const config * new_cfg)
2677{
2678 const theme::status_item *item = theme_.get_status_item(report_name);
2679 if (!item) {
2680 // This should be a warning, but unfortunately there are too many
2681 // unused reports to easily deal with.
2682 //WRN_DP << "no report '" << report_name << "' in theme";
2683 return;
2684 }
2685
2686 // Now we will need the config. Generate one if needed.
2687
2689
2692 }
2693
2694 reports::context temp_context = reports::context(*dc_, *this, *resources::tod_manager, wb_.lock(), mhb);
2695
2696 const config generated_cfg = new_cfg ? config() : reports_object_->generate_report(report_name, temp_context);
2697 if ( new_cfg == nullptr )
2698 new_cfg = &generated_cfg;
2699
2700 rect& loc = reportLocations_[report_name];
2701 const rect& new_loc = item->location(video::game_canvas());
2702 config &report = reports_[report_name];
2703
2704 // Report and its location is unchanged since last time. Do nothing.
2705 if (loc == new_loc && report == *new_cfg) {
2706 return;
2707 }
2708
2709 DBG_DP << "updating report: " << report_name;
2710
2711 // Mark both old and new locations for redraw.
2714
2715 // Update the config and current location.
2716 report = *new_cfg;
2717 loc = new_loc;
2718
2719 // Not 100% sure this is okay
2720 // but it seems to be working so i'm not changing it.
2722
2723 if (report.empty()) return;
2724
2725 // Add prefix, postfix elements.
2726 // Make sure that they get the same tooltip
2727 // as the guys around them.
2728 std::string str = item->prefix();
2729 if (!str.empty()) {
2730 config &e = report.add_child_at("element", config(), 0);
2731 e["text"] = str;
2732 e["tooltip"] = report.mandatory_child("element")["tooltip"];
2733 }
2734 str = item->postfix();
2735 if (!str.empty()) {
2736 config &e = report.add_child("element");
2737 e["text"] = str;
2738 e["tooltip"] = report.mandatory_child("element", -1)["tooltip"];
2739 }
2740
2741 // Do a fake run of drawing the report, so tooltips can be determined.
2742 // TODO: this is horrible, refactor reports to actually make sense
2743 draw_report(report_name, true);
2744}
2745
2746void display::draw_report(const std::string& report_name, bool tooltip_test)
2747{
2748 const theme::status_item *item = theme_.get_status_item(report_name);
2749 if (!item) {
2750 // This should be a warning, but unfortunately there are too many
2751 // unused reports to easily deal with.
2752 //WRN_DP << "no report '" << report_name << "' in theme";
2753 return;
2754 }
2755
2756 const rect& loc = reportLocations_[report_name];
2757 const config& report = reports_[report_name];
2758
2759 int x = loc.x, y = loc.y;
2760
2761 // Loop through and display each report element.
2762 int tallest = 0;
2763 int image_count = 0;
2764 bool used_ellipsis = false;
2765 std::ostringstream ellipsis_tooltip;
2766 rect ellipsis_area = loc;
2767
2768 for (config::const_child_itors elements = report.child_range("element");
2769 elements.begin() != elements.end(); elements.pop_front())
2770 {
2771 rect area {x, y, loc.w + loc.x - x, loc.h + loc.y - y};
2772 if (area.h <= 0) break;
2773
2774 std::string t = elements.front()["text"];
2775 if (!t.empty())
2776 {
2777 if (used_ellipsis) goto skip_element;
2778
2779 // Draw a text element.
2781 bool eol = false;
2782 if (t[t.size() - 1] == '\n') {
2783 eol = true;
2784 t = t.substr(0, t.size() - 1);
2785 }
2786 // If stripping left the text empty, skip it.
2787 if (t.empty()) {
2788 // Blank text has a null size when rendered.
2789 // It does not, however, have a null size when the size
2790 // is requested with get_size(). Hence this check.
2791 continue;
2792 }
2793 text.set_link_aware(false)
2794 .set_text(t, true);
2796 .set_font_size(item->font_size())
2798 .set_alignment(PANGO_ALIGN_LEFT)
2800 .set_maximum_width(area.w)
2801 .set_maximum_height(area.h, false)
2802 .set_ellipse_mode(PANGO_ELLIPSIZE_END)
2804
2805 point tsize = text.get_size();
2806
2807 // check if next element is text with almost no space to show it
2808 const int minimal_text = 12; // width in pixels
2809 config::const_child_iterator ee = elements.begin();
2810 if (!eol && loc.w - (x - loc.x + tsize.x) < minimal_text &&
2811 ++ee != elements.end() && !(*ee)["text"].empty())
2812 {
2813 // make this element longer to trigger rendering of ellipsis
2814 // (to indicate that next elements have not enough space)
2815 //NOTE this space should be longer than minimal_text pixels
2816 t = t + " ";
2817 text.set_text(t, true);
2818 tsize = text.get_size();
2819 // use the area of this element for next tooltips
2820 used_ellipsis = true;
2821 ellipsis_area.x = x;
2822 ellipsis_area.y = y;
2823 ellipsis_area.w = tsize.x;
2824 ellipsis_area.h = tsize.y;
2825 }
2826
2827 area.w = tsize.x;
2828 area.h = tsize.y;
2829 if (!tooltip_test) {
2830 draw::blit(text.render_and_get_texture(), area);
2831 }
2832 if (area.h > tallest) {
2833 tallest = area.h;
2834 }
2835 if (eol) {
2836 x = loc.x;
2837 y += tallest;
2838 tallest = 0;
2839 } else {
2840 x += area.w;
2841 }
2842 }
2843 else if (!(t = elements.front()["image"].str()).empty())
2844 {
2845 if (used_ellipsis) goto skip_element;
2846
2847 // Draw an image element.
2849
2850 if (!img) {
2851 ERR_DP << "could not find image for report: '" << t << "'";
2852 continue;
2853 }
2854
2855 if (area.w < img.w() && image_count) {
2856 // We have more than one image, and this one doesn't fit.
2858 used_ellipsis = true;
2859 }
2860
2861 if (img.w() < area.w) area.w = img.w();
2862 if (img.h() < area.h) area.h = img.h();
2863 if (!tooltip_test) {
2864 draw::blit(img, area);
2865 }
2866
2867 ++image_count;
2868 if (area.h > tallest) {
2869 tallest = area.h;
2870 }
2871
2872 if (!used_ellipsis) {
2873 x += area.w;
2874 } else {
2875 ellipsis_area = area;
2876 }
2877 }
2878 else
2879 {
2880 // No text nor image, skip this element
2881 continue;
2882 }
2883
2884 skip_element:
2885 t = elements.front()["tooltip"].t_str().c_str();
2886 if (!t.empty()) {
2887 if (tooltip_test && !used_ellipsis) {
2888 tooltips::add_tooltip(area, t, elements.front()["help"].t_str().c_str());
2889 } else {
2890 // Collect all tooltips for the ellipsis.
2891 // TODO: need a better separator
2892 // TODO: assign an action
2893 ellipsis_tooltip << t;
2894 config::const_child_iterator ee = elements.begin();
2895 if (++ee != elements.end())
2896 ellipsis_tooltip << "\n _________\n\n";
2897 }
2898 }
2899 }
2900
2901 if (tooltip_test && used_ellipsis) {
2902 tooltips::add_tooltip(ellipsis_area, ellipsis_tooltip.str());
2903 }
2904}
2905
2906bool display::draw_reports(const rect& region)
2907{
2908 bool drew = false;
2909 for(const auto& it : reports_) {
2910 const std::string& name = it.first;
2911 const rect& loc = reportLocations_[name];
2912 if(loc.overlaps(region)) {
2913 draw_report(name);
2914 drew = true;
2915 }
2916 }
2917 return drew;
2918}
2919
2921{
2922 DBG_DP << "invalidate_all()";
2923 invalidateAll_ = true;
2924 invalidated_.clear();
2925}
2926
2928{
2930 return false;
2931
2932 bool tmp;
2933 tmp = invalidated_.insert(loc).second;
2934 return tmp;
2935}
2936
2937bool display::invalidate(const std::set<map_location>& locs)
2938{
2940 return false;
2941 bool ret = false;
2942 for (const map_location& loc : locs) {
2943 ret = invalidated_.insert(loc).second || ret;
2944 }
2945 return ret;
2946}
2947
2948bool display::propagate_invalidation(const std::set<map_location>& locs)
2949{
2950 if(invalidateAll_)
2951 return false;
2952
2953 if(locs.size()<=1)
2954 return false; // propagation never needed
2955
2956 bool result = false;
2957 {
2958 // search the first hex invalidated (if any)
2959 std::set<map_location>::const_iterator i = locs.begin();
2960 for(; i != locs.end() && invalidated_.count(*i) == 0 ; ++i) {}
2961
2962 if (i != locs.end()) {
2963
2964 // propagate invalidation
2965 // 'i' is already in, but I suspect that splitting the range is bad
2966 // especially because locs are often adjacents
2967 std::size_t previous_size = invalidated_.size();
2968 invalidated_.insert(locs.begin(), locs.end());
2969 result = previous_size < invalidated_.size();
2970 }
2971 }
2972 return result;
2973}
2974
2979
2981{
2983 return false;
2984
2985 DBG_DP << "invalidating locations in " << rect;
2986
2987 bool result = false;
2988 for(const map_location& loc : hexes_under_rect(rect)) {
2989 //DBG_DP << "invalidating " << loc.x << ',' << loc.y;
2990 result |= invalidate(loc);
2991 }
2992 return result;
2993}
2994
2996{
2997 if(context().map().is_village(loc)) {
2998 const int owner = context().village_owner(loc) - 1;
2999 if(owner >= 0 && flags_[owner].need_update()
3000 && (!fogged(loc) || !viewing_team().is_enemy(owner + 1))) {
3001 invalidate(loc);
3002 }
3003 }
3004}
3005
3007{
3009 animate_map_ = prefs::get().animate_map();
3010 if(animate_map_) {
3011 for(const map_location& loc : get_visible_hexes()) {
3012 if(shrouded(loc))
3013 continue;
3014 if(builder_->update_animation(loc)) {
3015 invalidate(loc);
3016 } else {
3018 }
3019 }
3020 }
3021
3022 for(const unit& u : context().units()) {
3023 u.anim_comp().refresh();
3024 }
3025 for(const unit* u : *fake_unit_man_) {
3026 u->anim_comp().refresh();
3027 }
3028
3029 bool new_inval;
3030 do {
3031 new_inval = false;
3032 for(const unit& u : context().units()) {
3033 new_inval |= u.anim_comp().invalidate(*this);
3034 }
3035 for(const unit* u : *fake_unit_man_) {
3036 new_inval |= u->anim_comp().invalidate(*this);
3037 }
3038 } while(new_inval);
3039
3040 halo_man_.update();
3041}
3042
3044{
3045 for(const unit & u : context().units()) {
3046 u.anim_comp().set_standing();
3047 }
3048}
3049
3051{
3052 for(const map_location& loc : arrow.get_path()) {
3053 arrows_map_[loc].push_back(&arrow);
3054 }
3055}
3056
3058{
3059 for(const map_location& loc : arrow.get_path()) {
3060 arrows_map_[loc].remove(&arrow);
3061 }
3062}
3063
3065{
3066 for(const map_location& loc : arrow.get_previous_path()) {
3067 arrows_map_[loc].remove(&arrow);
3068 }
3069
3070 for(const map_location& loc : arrow.get_path()) {
3071 arrows_map_[loc].push_back(&arrow);
3072 }
3073}
3074
3076{
3077 auto [center_x, center_y] = viewport_origin_ + map_area().center();
3078 return pixel_position_to_hex(center_x, center_y);
3079}
3080
3082{
3083 cfg["view_locked"] = view_locked_;
3084 cfg["color_adjust_red"] = color_adjust_.r;
3085 cfg["color_adjust_green"] = color_adjust_.g;
3086 cfg["color_adjust_blue"] = color_adjust_.b;
3087 get_middle_location().write(cfg.add_child("location"));
3088}
3089
3091{
3092 view_locked_ = cfg["view_locked"].to_bool(false);
3093 color_adjust_.r = cfg["color_adjust_red"].to_int(0);
3094 color_adjust_.g = cfg["color_adjust_green"].to_int(0);
3095 color_adjust_.b = cfg["color_adjust_blue"].to_int(0);
3096}
3097
3099{
3100 if (!reach_map_changed_) return;
3101 if (reach_map_.empty() != reach_map_old_.empty()) {
3102 // Invalidate everything, since we both brighten and darken
3103 for (const auto& hex : get_visible_hexes()) {
3104 invalidate(hex);
3105 }
3106 } else if (!reach_map_.empty()) {
3107 // Invalidate new and old reach
3108 reach_map::iterator reach, reach_old;
3109 for (reach = reach_map_.begin(); reach != reach_map_.end(); ++reach) {
3110 invalidate(reach->first);
3111 }
3112 for (reach_old = reach_map_old_.begin(); reach_old != reach_map_old_.end(); ++reach_old) {
3113 invalidate(reach_old->first);
3114 }
3115 }
3117 reach_map_changed_ = false;
3118
3119 // Make sure there are teams before trying to access units.
3120 if(!context().teams().empty()){
3121 // Update the reachmap-context team, the selected unit's team shall override the displayed unit's.
3122 if(context().units().count(selectedHex_)) {
3124 } else if(context().get_visible_unit(mouseoverHex_, viewing_team()) != nullptr){
3126 } else {
3127 /**
3128 * If no unit is selected or displayed, the reachmap-context team should failsafe to
3129 * the viewing team index, this makes sure the team is invalid when getting the reachmap
3130 * images in game_display::get_reachmap_images().
3131 */
3133 }
3134 DBG_DP << "Updated reachmap context team index to " << std::to_string(reach_map_team_index_);
3135 }
3136}
3137
3138display *display::singleton_ = nullptr;
static bool is_enemy(std::size_t side, std::size_t other_side)
map_location loc
Definition move.cpp:172
#define LOG_DP
void update_animation_timers(double acceleration)
Updates both animation timelines.
Definition animated.cpp:38
Arrows destined to be drawn on the map.
double t
std::vector< std::string > names
Definitions for the terrain builder.
void add_frame(const std::chrono::milliseconds &duration, const T &value, bool force_change=false)
Appends a frame, starting where the previous one ends (or at start_time, if first).
std::chrono::milliseconds get_end_time() const
Animation-time offset where the last frame ends.
Arrows destined to be drawn on the map.
Definition arrow.hpp:30
const arrow_path_t & get_previous_path() const
Definition arrow.cpp:127
const arrow_path_t & get_path() const
Definition arrow.cpp:122
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
config & add_child(std::string_view key)
Definition config.cpp:436
child_itors child_range(std::string_view key)
Definition config.cpp:268
config & add_child_at(std::string_view key, const config &val, std::size_t index)
Definition config.cpp:465
boost::iterator_range< const_child_iterator > const_child_itors
Definition config.hpp:281
bool empty() const
Definition config.cpp:823
config & mandatory_child(std::string_view key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
Definition config.cpp:362
Abstract class for exposing game data that doesn't depend on the GUI, however which for historical re...
virtual const gamemap & map() const =0
virtual const std::vector< team > & teams() const =0
int village_owner(const map_location &loc) const
Given the location of a village, will return the 1-based number of the team that currently owns it,...
const unit * get_visible_unit(const map_location &loc, const team &current_team, bool see_all=false) const
virtual const unit_map & units() const =0
Sort-of-Singleton that many classes, both GUI and non-GUI, use to access the game data.
Definition display.hpp:88
const team & viewing_team() const
Definition display.cpp:337
void unhide_buttons()
Unhide theme buttons so they draw again.
Definition display.cpp:928
void set_viewing_team_index(std::size_t team, bool observe=false)
Sets the team controlled by the player using the computer.
Definition display.cpp:342
bool map_screenshot_
Used to indicate to drawing functions that we are doing a map screenshot.
Definition display.hpp:862
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:1358
void layout_buttons()
Definition display.cpp:788
bool redraw_background_
Definition display.hpp:736
void update_render_textures()
Ensure render textures are valid and correct.
Definition display.cpp:2331
bool invalidate_locations_in_rect(const rect &rect)
invalidate all hexes under the rectangle rect (in screen coordinates)
Definition display.cpp:2980
static unsigned int last_zoom_
The previous value of zoom_.
Definition display.hpp:731
std::size_t viewing_team_index_
Definition display.hpp:713
void write(config &cfg) const
Definition display.cpp:3081
static bool zoom_at_min()
Definition display.cpp:1638
void get_terrain_images(const map_location &loc, const std::string &timeid, TERRAIN_TYPE terrain_type)
Definition display.cpp:1022
void remove_overlay(const map_location &loc)
remove_overlay will remove all overlays on a tile.
Definition display.cpp:131
map_location selectedHex_
Definition display.hpp:761
void recalculate_minimap()
Schedule the minimap for recalculation.
Definition display.cpp:1465
bool unit_can_draw_here(const map_location &loc, const unit &unit) const
Returns true if there is no exclusive draw request for loc, or if there is, that it's for unit.
Definition display.cpp:381
void redraw_minimap()
Schedule the minimap to be redrawn.
Definition display.cpp:1486
point get_location(const map_location &loc) const
Functions to get the on-screen positions of hexes.
Definition display.cpp:674
virtual void render() override
Update offscreen render buffers.
Definition display.cpp:2251
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:141
void fade_tod_mask(const std::string &old, const std::string &new_)
ToD mask smooth fade.
Definition display.cpp:2046
bool add_exclusive_draw(const map_location &loc, const unit &unit)
Allows a unit to request to be the only one drawn in its hex.
Definition display.cpp:366
bool invalidate(const map_location &loc)
Function to invalidate a specific tile for redrawing.
Definition display.cpp:2927
void set_playing_team_index(std::size_t team)
sets the team whose turn it currently is
Definition display.cpp:359
uint8_t tod_hex_alpha1
Definition display.hpp:756
const team & playing_team() const
Definition display.cpp:332
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:1449
bool view_locked_
Definition display.hpp:722
double turbo_speed() const
Definition display.cpp:1965
@ ONSCREEN_WARP
Definition display.hpp:495
void scroll_to_xy(const point &screen_coordinates, SCROLL_TYPE scroll_type, bool force=true)
Definition display.cpp:1737
int invalidated_hexes_
Count work done for the debug info displayed under fps.
Definition display.hpp:890
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:400
void set_fade(const color_t &color)
Definition display.cpp:2100
void queue_repaint()
Queues repainting to the screen, but doesn't rerender.
Definition display.cpp:2157
bool reach_map_changed_
Definition display.hpp:879
static int hex_size()
Function which returns the size of a hex in pixels (from left tip to right tip or top edge to bottom ...
Definition display.hpp:259
static double get_zoom_factor()
Returns the current zoom factor.
Definition display.hpp:262
const rect & unit_image_area() const
Definition display.cpp:468
@ FOREGROUND
Definition display.hpp:701
@ BACKGROUND
Definition display.hpp:701
bool propagate_invalidation(const std::set< map_location > &locs)
If this set is partially invalidated, invalidate all its hexes.
Definition display.cpp:2948
void clear_redraw_observers()
Clear the redraw observers.
Definition display.cpp:2169
void invalidate_game_status()
Function to invalidate the game status displayed on the sidebar.
Definition display.hpp:308
const theme::action * action_pressed()
Definition display.cpp:1417
static submerge_data get_submerge_data(const rect &dest, double submerge, const point &size, uint8_t alpha, bool hreverse, bool vreverse)
Definition display.cpp:1993
std::shared_ptr< gui::button > find_action_button(const std::string &id)
Retrieves a pointer to a theme UI button.
Definition display.cpp:768
void set_theme(const std::string &new_theme)
Definition display.cpp:246
void rebuild_all()
Rebuild all dynamic terrain.
Definition display.cpp:428
void change_display_context(const display_context *dc)
Definition display.cpp:439
void set_prevent_draw(bool pd=true)
Prevent the game display from drawing.
Definition display.cpp:1979
virtual const time_of_day & get_time_of_day(const map_location &loc=map_location::null_location()) const =0
theme theme_
Definition display.hpp:723
tod_color color_adjust_
Definition display.hpp:938
bool get_prevent_draw()
Definition display.cpp:1988
virtual void layout() override
Finalize screen layout.
Definition display.cpp:2220
virtual void highlight_hex(map_location hex)
Definition display.cpp:1389
void update_tod(const time_of_day *tod_override=nullptr)
Applies r,g,b coloring to the map.
Definition display.cpp:387
void add_redraw_observer(const std::function< void(display &)> &f)
Adds a redraw observer, a function object to be called when a full rerender is queued.
Definition display.cpp:2164
bool invalidate_visible_locations_in_rect(const rect &rect)
Definition display.cpp:2975
static bool zoom_at_max()
Definition display.cpp:1633
static display * singleton_
Definition display.hpp:941
std::shared_ptr< gui::button > find_menu_button(const std::string &id)
Definition display.cpp:778
void render_map_outside_area()
Draw/redraw the off-map background area.
Definition display.cpp:2376
map_labels & labels()
Definition display.cpp:2392
std::size_t playing_team_index() const
The playing team is the team whose turn it is.
Definition display.hpp:107
void remove_arrow(arrow &)
Definition display.cpp:3057
@ DEBUG_COORDINATES
Overlays x,y coords on tiles.
Definition display.hpp:898
@ DEBUG_BENCHMARK
Toggle to continuously redraw the whole map.
Definition display.hpp:910
@ DEBUG_NUM_BITMAPS
Overlays number of bitmaps on tiles.
Definition display.hpp:904
@ NUM_DEBUG_FLAGS
Dummy entry to size the bitmask.
Definition display.hpp:913
@ DEBUG_FOREGROUND
Separates background and foreground terrain layers.
Definition display.hpp:907
@ DEBUG_TERRAIN_CODES
Overlays terrain codes on tiles.
Definition display.hpp:901
void process_reachmap_changes()
Definition display.cpp:3098
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:1815
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:547
void invalidate_animations_location(const map_location &loc)
Per-location invalidation called by invalidate_animations() Extra game per-location invalidation (vil...
Definition display.cpp:2995
map_location mouseoverHex_
Definition display.hpp:762
bool fogged(const map_location &loc) const
Returns true if location (x,y) is covered in fog.
Definition display.cpp:669
bool set_zoom(bool increase)
Zooms the display in (true) or out (false).
Definition display.cpp:1643
std::map< std::string, rect > reportLocations_
Definition display.hpp:747
const rect_of_hexes get_visible_hexes() const
Returns the rectangular area of visible hexes.
Definition display.hpp:357
bool invalidateAll_
Definition display.hpp:737
int drawn_hexes_
Definition display.hpp:891
void draw_overlays_at(const map_location &loc)
Definition display.cpp:2603
texture get_flag(const map_location &loc)
Definition display.cpp:313
void invalidate_all()
Function to invalidate all tiles.
Definition display.cpp:2920
std::map< std::string, config > reports_
Definition display.hpp:749
point viewport_origin_
Position of the top-left corner of the viewport, in pixels.
Definition display.hpp:721
map_location get_middle_location() const
Definition display.cpp:3075
void bounds_check_position()
Definition display.cpp:1947
std::function< rect(rect)> minimap_renderer_
Definition display.hpp:734
surface screenshot(bool map_screenshot=false)
Capture a (map-)screenshot into a surface.
Definition display.cpp:723
void init_flags()
Init the flag list and the team colors used by ~TC.
Definition display.cpp:257
std::vector< std::shared_ptr< gui::button > > action_buttons_
Definition display.hpp:750
void drawing_buffer_add(const drawing_layer layer, const map_location &loc, decltype(draw_helper::do_draw) draw_func)
Add an item to the drawing buffer.
Definition display.cpp:1241
rect map_outside_area() const
Returns the available area for a map, this may differ from the above.
Definition display.cpp:516
std::size_t reach_map_team_index_
Definition display.hpp:881
bool prevent_draw_
Definition display.hpp:548
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:676
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:1728
void reload_map()
Updates internals that cache map size.
Definition display.cpp:433
rect max_map_area() const
Returns the maximum area used for the map regardless to resolution and view size.
Definition display.cpp:473
bool tile_fully_on_screen(const map_location &loc) const
Check if a tile is fully visible on screen.
Definition display.cpp:1723
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:691
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:1825
bool is_blindfolded() const
Definition display.cpp:453
std::vector< texture > terrain_image_vector_
Definition display.hpp:780
std::vector< std::string > fog_images_
Definition display.hpp:758
void fade_to(const color_t &color, const std::chrono::milliseconds &duration)
Screen fade.
Definition display.cpp:2068
texture tod_hex_mask2
Definition display.hpp:755
std::map< map_location, std::list< arrow * > > arrows_map_
Maps the list of arrows for each location.
Definition display.hpp:936
void add_overlay(const map_location &loc, overlay &&ov)
Functions to add and remove overlays from locations.
Definition display.cpp:118
std::vector< std::function< void(display &)> > redraw_observers_
Definition display.hpp:893
void read(const config &cfg)
Definition display.cpp:3090
const theme::menu * menu_pressed()
Definition display.cpp:1433
const rect_of_hexes hexes_under_rect(const rect &r) const
Return the rectangular area of hexes overlapped by r (r is in screen coordinates)
Definition display.cpp:621
const std::unique_ptr< terrain_builder > builder_
Definition display.hpp:733
std::vector< animated< image::locator > > flags_
Animated flags for each team.
Definition display.hpp:776
void remove_overlays()
remove_overlays will remove all overlays on the map.
Definition display.cpp:136
rect get_location_rect(const map_location &loc) const
Returns the on-screen rect corresponding to a loc.
Definition display.cpp:685
static void fill_images_list(const std::string &prefix, std::vector< std::string > &images)
Definition display.cpp:406
void draw_report(const std::string &report_name, bool test_run=false)
Draw the specified report.
Definition display.cpp:2746
std::set< map_location > invalidated_
Definition display.hpp:751
color_t fade_color_
Definition display.hpp:559
std::vector< texture > get_fog_shroud_images(const map_location &loc, image::TYPE image_type)
Definition display.cpp:938
void queue_rerender()
Marks everything for rendering including all tiles and sidebar.
Definition display.cpp:2105
rect minimap_location_
Definition display.hpp:735
void create_buttons()
Definition display.cpp:831
int blindfold_ctr_
Definition display.hpp:666
std::string remove_exclusive_draw(const map_location &loc)
Cancels an exclusive draw request.
Definition display.cpp:373
bool invalidateGameStatus_
Definition display.hpp:739
reach_map reach_map_old_
Definition display.hpp:878
virtual void draw_invalidated()
Only called when there's actual redrawing to do.
Definition display.cpp:2407
void drawing_buffer_commit()
Draws the drawing_buffer_ and clears it.
Definition display.cpp:1246
rect map_area() const
Returns the area used for the map.
Definition display.cpp:490
int zoom_index_
Definition display.hpp:729
void draw_panel(const theme::panel &panel)
Definition display.cpp:1275
void draw_buttons()
Definition display.cpp:896
static int hex_width()
Function which returns the "average" width of a hex in pixels, up to where the next hex starts (half ...
Definition display.hpp:253
void reset_standing_animations()
Definition display.cpp:3043
bool animate_water_
Local version of prefs::get().animate_water, used to detect when it's changed.
Definition display.hpp:769
bool debug_flag_set(DEBUG_FLAG flag) const
Definition display.hpp:916
void toggle_default_zoom()
Sets the zoom amount to the default.
Definition display.cpp:1711
virtual rect get_clip_rect() const
Get the clipping rectangle for drawing.
Definition display.cpp:2402
CKey keys_
Definition display.hpp:763
reports * reports_object_
Definition display.hpp:741
bool draw_reports(const rect &region)
Draw all reports in the given region.
Definition display.cpp:2906
void invalidate_animations()
Function to invalidate animated terrains and units which may have changed.
Definition display.cpp:3006
virtual rect screen_location() override
Return the current draw location of the display, on the screen.
Definition display.cpp:2323
void hide_buttons()
Hide theme buttons so they don't draw.
Definition display.cpp:918
virtual bool expose(const rect &region) override
Paint the indicated region to the screen.
Definition display.cpp:2278
std::list< draw_helper > drawing_buffer_
Definition display.hpp:835
uint8_t tod_hex_alpha2
Definition display.hpp:757
std::vector< std::shared_ptr< gui::button > > menu_buttons_
Definition display.hpp:750
virtual void update() override
Update animations and internal state.
Definition display.cpp:2203
void draw()
Perform rendering of invalidated items.
Definition display.cpp:2174
const rect & minimap_area() const
mapx is the width of the portion of the display which shows the game area.
Definition display.cpp:458
texture back_
Definition display.hpp:584
events::generic_event scroll_event_
Event raised when the map is being scrolled.
Definition display.hpp:744
bool show_everything() const
Definition display.hpp:104
virtual void draw_hex(const map_location &loc)
Redraws a single gamemap location.
Definition display.cpp:2443
texture tod_hex_mask1
Definition display.hpp:754
virtual ~display()
Definition display.cpp:236
const rect & palette_area() const
Definition display.cpp:463
std::size_t playing_team_index_
Definition display.hpp:794
halo::manager halo_man_
Definition display.hpp:671
void reinit_flags_for_team(const team &)
Rebuild the flag list (not team colors) for a single side.
Definition display.cpp:268
void draw_minimap()
Actually draw the minimap.
Definition display.cpp:1491
texture front_
Render textures, for intermediate rendering.
Definition display.hpp:583
std::size_t viewing_team_index() const
The viewing team is the team currently viewing the game.
Definition display.hpp:117
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:533
void update_arrow(arrow &a)
Called by arrow objects when they change.
Definition display.cpp:3064
void draw_label(const theme::label &label)
Definition display.cpp:1300
const std::unique_ptr< fake_unit_manager > fake_unit_man_
Definition display.hpp:732
const display_context * dc_
Definition display.hpp:670
static bool outside_area(const 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:525
bool animate_map_
Local cache for prefs::get().animate_map, since it is constantly queried.
Definition display.hpp:766
std::vector< std::string > shroud_images_
Definition display.hpp:759
bool scroll(const point &amount, bool force=false)
Scrolls the display by amount pixels.
Definition display.cpp:1540
static unsigned int zoom_
The current zoom, in pixels (on screen) per 72 pixels (in the graphic assets), i.e....
Definition display.hpp:728
bool shrouded(const map_location &loc) const
Returns true if location (x,y) is covered in shroud.
Definition display.cpp:664
bool dont_show_all_
Definition display.hpp:714
std::bitset< NUM_DEBUG_FLAGS > debug_flags_
Currently set debug flags.
Definition display.hpp:933
void blindfold(bool flag)
Definition display.cpp:445
void refresh_report(const std::string &report_name, const config *new_cfg=nullptr)
Update the given report.
Definition display.cpp:2676
std::weak_ptr< wb::manager > wb_
Definition display.hpp:672
void add_arrow(arrow &)
Definition display.cpp:3050
const std::unique_ptr< map_labels > map_labels_
Definition display.hpp:740
void set_diagnostic(const std::string &msg)
Definition display.cpp:1399
const display_context & context() const
Definition display.hpp:187
virtual void select_hex(map_location hex)
Definition display.cpp:1381
int diagnostic_label_
Definition display.hpp:738
std::map< std::string, texture > reportSurfaces_
Definition display.hpp:748
virtual overlay_map & get_overlays()=0
bool draw_all_panels(const rect &region)
Redraws all panels intersecting the given region.
Definition display.cpp:1336
reach_map reach_map_
Definition display.hpp:877
display(const display_context *dc, std::weak_ptr< wb::manager > wb, reports &reports_object, const std::string &theme_id, const config &level)
Definition display.cpp:147
virtual void notify_observers()
Manages a list of fake units for the display object.
void set_position(double xpos, double ypos)
void set_lifetime(const std::chrono::milliseconds &lifetime, const std::chrono::milliseconds &fadeout=std::chrono::milliseconds{100})
void set_color(const color_t &color)
void set_clip_rect(const rect &r)
void set_font_size(int font_size)
Text class.
Definition text.hpp:78
pango_text & set_font_style(const FONT_STYLE font_style)
Definition text.cpp:381
point get_size()
Returns the size of the text, in drawing coordinates.
Definition text.cpp:154
pango_text & set_characters_per_line(const unsigned characters_per_line)
Definition text.cpp:416
pango_text & set_foreground_color(const color_t &color)
Definition text.cpp:391
pango_text & set_family_class(font::family_class fclass)
Definition text.cpp:359
pango_text & set_ellipse_mode(const PangoEllipsizeMode ellipse_mode)
Definition text.cpp:452
pango_text & set_alignment(const PangoAlignment alignment)
Definition text.cpp:472
pango_text & set_font_size(unsigned font_size)
Definition text.cpp:369
pango_text & set_link_aware(bool b)
Definition text.cpp:495
bool set_text(const std::string &text, const bool markedup)
Sets the text to render.
Definition text.cpp:333
pango_text & set_maximum_height(int height, bool multiline)
Definition text.cpp:427
texture render_and_get_texture()
Returns the cached texture, or creates a new one otherwise.
Definition text.cpp:122
std::ostringstream wrapper.
Definition formatter.hpp:40
terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition map.cpp:265
int w() const
Effective map width.
Definition map.hpp:50
int h() const
Effective map height.
Definition map.hpp:53
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition map.cpp:347
const terrain_type & get_terrain_info(const t_translation::terrain_code &terrain) const
Definition map.cpp:78
@ DEFAULT_SPACE
Definition button.hpp:32
void update()
Process animations, remove deleted halos, and invalidate screen regions now requiring redraw.
Definition halo.cpp:428
handle add(int x, int y, const std::string &image, const map_location &loc, halo::ORIENTATION orientation=NORMAL, bool infinite=true)
Add a haloing effect using 'image centered on (x,y).
Definition halo.cpp:408
void render(const rect &r)
Render halos in region.
Definition halo.cpp:433
Generic locator abstracting the location of an image.
Definition picture.hpp:59
A RAII object to temporary leave the synced context like in wesnoth.synchronize_choice.
void recalculate_shroud()
Definition label.cpp:279
void set_team(const team *)
Definition label.cpp:140
void recalculate_labels()
Definition label.cpp:246
events::mouse_handler & get_mouse_handler_base() override
Get a reference to a mouse handler member a derived class uses.
hotkey::command_executor * get_hotkey_command_executor() override
Optionally get a command executor to handle context menu events.
static prefs & get()
int scroll_speed()
bool turbo()
static rng & default_instance()
Definition random.cpp:73
int get_random_int(int min, int max)
Definition random.hpp:51
config generate_report(const std::string &name, const context &ct)
Generate the specified report using the given context.
Definition reports.cpp:1897
An object to leave the synced context during draw or unsynced wml items when we don’t know whether we...
This class stores all the data for a single 'side' (in game nomenclature).
Definition team.hpp:74
bool is_enemy(int n) const
Definition team.hpp:270
bool shrouded(const map_location &loc) const
Definition team.cpp:636
bool fogged(const map_location &loc) const
Definition team.cpp:645
The class terrain_builder is constructed from a config object, and a gamemap object.
Definition builder.hpp:48
std::vector< animated< image::locator > > imagelist
A shorthand typedef for a list of animated image locators, the base data type returned by the get_ter...
Definition builder.hpp:74
TERRAIN_TYPE
Used as a parameter for the get_terrain_at function.
Definition builder.hpp:51
@ BACKGROUND
Represents terrains which are to be drawn behind unit sprites.
Definition builder.hpp:52
@ FOREGROUND
Represents terrains which are to be drawn in front of them.
Definition builder.hpp:56
double unit_submerge() const
Definition terrain.hpp:148
Wrapper class to encapsulate creation and management of an SDL_Texture.
Definition texture.hpp:33
void reset()
Releases ownership of the managed texture and resets the ptr to null.
Definition texture.cpp:202
void set_src(const rect &r)
Set the source region of the texture used for drawing operations.
Definition texture.cpp:122
point draw_size() const
The size of the texture in draw-space.
Definition texture.hpp:120
void set_draw_size(int w, int h)
Set the intended size of the texture, in draw-space.
Definition texture.hpp:129
point get_raw_size() const
The raw internal texture size.
Definition texture.cpp:109
void set_alpha_mod(uint8_t alpha)
Alpha modifier.
Definition texture.cpp:146
void clear_src()
Clear the source region.
Definition texture.hpp:165
const std::string & get_id() const
Definition theme.hpp:54
const std::string & image() const
Definition theme.hpp:157
virtual rect & location(const rect &screen) const
Definition theme.cpp:333
color_t font_rgb() const
Definition theme.hpp:139
std::size_t font_size() const
Definition theme.hpp:138
bool font_rgb_set() const
Definition theme.hpp:140
virtual rect & location(const rect &screen) const
Definition theme.cpp:333
const std::string & prefix() const
Definition theme.hpp:132
const std::string & postfix() const
Definition theme.hpp:133
const std::vector< menu > & menus() const
Definition theme.hpp:256
const border_t & border() const
Definition theme.hpp:282
const rect & palette_location(const rect &screen) const
Definition theme.hpp:279
static NOT_DANGLING const config & get_theme_config(const std::string &id)
Returns the saved config for the theme with the given ID.
Definition theme.cpp:1017
bool set_resolution(const rect &screen)
Definition theme.cpp:618
const menu * get_menu_item(const std::string &key) const
Definition theme.cpp:931
const rect & mini_map_location(const rect &screen) const
Definition theme.hpp:275
const std::vector< action > & actions() const
Definition theme.hpp:258
const std::vector< label > & labels() const
Definition theme.hpp:255
const rect & main_map_location(const rect &screen) const
Definition theme.hpp:273
const std::vector< panel > & panels() const
Definition theme.hpp:254
const status_item * get_status_item(const std::string &item) const
Definition theme.cpp:922
const rect & unit_image_location(const rect &screen) const
Definition theme.hpp:277
unit_iterator find(std::size_t id)
Definition map.cpp:302
This class represents a single unit of a specific type.
Definition unit.hpp:39
A simple wrapper class for optional reference types.
constexpr uint8_t ALPHA_OPAQUE
Definition color.hpp:37
constexpr uint8_t float_to_color(double n)
Convert a double in the range [0.0,1.0] to an 8-bit colour value.
Definition color.hpp:274
#define ERR_DP
#define MaxZoom
Definition display.cpp:87
#define final_zoom_index
Definition display.cpp:83
static int get_zoom_levels_index(unsigned int zoom_level)
Definition display.cpp:98
#define SmallZoom
Definition display.cpp:85
#define WRN_DP
Definition display.cpp:77
#define DefaultZoom
Definition display.cpp:84
static lg::log_domain log_display("display")
#define MinZoom
Definition display.cpp:86
#define zoom_levels
Definition display.cpp:82
#define DBG_DP
Definition display.cpp:79
map_display and display: classes which take care of displaying the map and game-data on the screen.
static SDL_Renderer * renderer()
Definition draw.cpp:33
Drawing functions, for drawing things on the screen.
drawing_layer
@ border
The border of the map.
@ terrain_bg
Terrain drawn behind the unit.
@ grid_bottom
Bottom half part of grid image.
@ unit_default
Default layer for drawing units.
@ reachmap_darken
Overlay on unreachable hexes.
@ unit_first
Reserve layers to be selected for wml.
@ terrain_fg
Terrain drawn in front of the unit.
@ fog_shroud
Fog and shroud.
@ unit_move_default
Default layer for drawing moving units.
@ arrows
Arrows from the arrows framework.
@ grid_top
Top half part of grid image.
const config * cfg
Declarations for File-IO.
const std::string & id() const
Gets this unit's id.
Definition unit.hpp:286
int side() const
The side this unit belongs to.
Definition unit.hpp:249
std::string label
What to show in the filter's drop-down list.
Definition manager.cpp:201
std::string id
Text to match against addon_info.tags()
Definition manager.cpp:199
void get_adjacent_tiles(const map_location &a, utils::span< map_location, 6 > res)
Function which, given a location, will place all adjacent locations in res.
Definition location.cpp:513
static std::ostream & output()
Definition log.cpp:103
Standard logging facilities (interface).
bool is_shrouded(const display *disp, const map_location &loc)
Our definition of map labels being obscured is if the tile is obscured, or the tile below is obscured...
Definition label.cpp:33
constexpr bool is_odd(T num)
Definition math.hpp:34
constexpr double normalize_progress(const std::chrono::duration< RepE, PeriodE > &elapsed, const std::chrono::duration< RepD, PeriodD > &duration)
Definition chrono.hpp:88
void invalidate_region(const rect &region)
Mark a region of the screen as requiring redraw.
void invalidate_all()
Mark the entire screen as requiring redraw.
render_target_setter set_render_target(const texture &t)
Set the given texture as the active render target.
Definition draw.cpp:757
void tiled(const texture &tex, const ::rect &dst, bool centered=false, bool mirrored=false)
Tile a texture to fill a region.
Definition draw.cpp:494
void rect(const ::rect &rect)
Draw a rectangle.
Definition draw.cpp:171
clip_setter reduce_clip(const ::rect &clip)
Set the clipping area to the intersection of the current clipping area and the given rectangle.
Definition draw.cpp:595
clip_setter override_clip(const ::rect &clip)
Override the clipping area.
Definition draw.cpp:590
void fill()
Definition draw.cpp:115
::rect get_clip()
Get the current clipping area, in draw coordinates.
Definition draw.cpp:615
void blit(const texture &tex, const ::rect &dst)
Draws a texture, or part of a texture, at the given location.
Definition draw.cpp:409
void draw()
Trigger a draw cycle.
Definition events.cpp:712
void pump_and_draw()
pump() then immediately draw()
Definition events.hpp:156
void pump()
Process all events currently in the queue.
Definition events.cpp:480
const int SIZE_FLOAT_LABEL
Definition constants.cpp:32
const color_t YELLOW_COLOR
pango_text & get_text_renderer()
Returns a reference to a static pango_text object.
Definition text.cpp:952
const int SIZE_PLUS
Definition constants.cpp:29
const int SIZE_TINY
Definition constants.cpp:23
int add_floating_label(const floating_label &flabel)
add a label floating on the screen above everything else.
const int SIZE_BUTTON_SMALL
Definition constants.cpp:26
void scroll_floating_labels(double xmove, double ymove)
moves all floating labels that have 'scroll_mode' set to ANCHOR_LABEL_MAP
void remove_floating_label(int handle, const std::chrono::milliseconds &fadeout)
removes the floating label given by 'handle' from the screen
void update_floating_labels()
void draw_floating_labels()
const color_t NORMAL_COLOR
std::string grid_bottom
std::string flag_rgb
std::string fog_prefix
const bool & debug
std::string shroud_prefix
bool is_in_dialog()
Is a dialog open?
Definition handler.cpp:1126
Definition halo.cpp:41
Functions to load and save images from/to disk.
bool exists(const image::locator &i_locator)
Returns true if the given image actually exists, without loading it.
Definition picture.cpp:855
std::function< rect(rect)> prep_minimap_for_rendering(const gamemap &map, const team *vw, const unit_map *units, const std::map< map_location, unsigned int > *reach_map, bool ignore_terrain_disabled)
Prepares the minimap texture and returns a function which will render it to the current rendering tar...
Definition minimap.cpp:40
TYPE
Used to specify the rendering format of images.
Definition picture.hpp:171
@ HEXED
Standard hexagonal tile mask applied, removing portions that don't fit.
Definition picture.hpp:175
@ TOD_COLORED
Same as HEXED, but with Time of Day color tint applied.
Definition picture.hpp:177
texture get_texture(const image::locator &i_locator, TYPE type, bool skip_cache)
Returns an image texture suitable for hardware-accelerated rendering.
Definition picture.cpp:955
texture get_lighted_texture(const image::locator &i_locator, const std::vector< light_adjust > &ls)
Definition picture.cpp:786
void set_color_adjustment(int r, int g, int b)
Changes Time of Day color tint for all applicable image types.
Definition picture.cpp:602
Unit and team statistics.
::tod_manager * tod_manager
Definition resources.cpp:29
fake_unit_manager * fake_units
Definition resources.cpp:30
play_controller * controller
Definition resources.cpp:21
STL namespace.
int add_tooltip(const rect &origin, const std::string &message, const std::string &action)
Definition tooltips.cpp:293
void clear_tooltips()
Definition tooltips.cpp:235
std::vector< std::string_view > split_view(std::string_view s, const char sep, const int flags)
void erase_if(Container &container, const Predicate &predicate)
Convenience wrapper for using std::remove_if on a container.
Definition general.hpp:108
std::vector< std::string > square_parenthetical_split(const std::string &val, const char separator, const std::string &left, const std::string &right, const int flags)
Similar to parenthetical_split, but also expands embedded square brackets.
std::vector< std::string > split(const config_attribute_value &val)
bool headless()
The game is running headless.
Definition video.cpp:146
rect game_canvas()
The game canvas area, in drawing coordinates.
Definition video.cpp:427
int get_pixel_scale()
Get the current active pixel scale multiplier.
Definition video.cpp:480
surface read_pixels(rect *r)
Copy back a portion of the render target that is already drawn.
Definition video.cpp:625
Definition display.hpp:45
int w
Definition pathfind.cpp:188
std::string_view data
Definition picture.cpp:188
static const unit * get_visible_unit(const reports::context &rc)
Definition reports.cpp:134
Transitional API for porting SDL_ttf-based code to Pango.
std::unique_ptr< MIX_Audio, decltype(&MIX_DestroyAudio)> value
Definition sound.cpp:139
rect dst
Location on the final composed sheet.
rect src
Non-transparent portion of the surface to compose.
The basic class for representing 8-bit RGB or RGBA colour values.
Definition color.hpp:51
constexpr color_t smooth_blend(const color_t &c, uint8_t p) const
Blend smoothly with another color_t.
Definition color.hpp:234
Holds options for calls to function 'announce' (announce).
Definition display.hpp:602
bool discard_previous
An announcement according these options should replace the previous announce (typical of fast announc...
Definition display.hpp:611
std::chrono::milliseconds lifetime
Lifetime measured in milliseconds.
Definition display.hpp:604
Helper for rendering the map by ordering draw operations.
Definition display.hpp:819
std::function< void(const rect &)> do_draw
Handles the actual drawing at this location.
Definition display.hpp:824
very simple iterator to walk into the rect_of_hexes
Definition display.hpp:327
iterator & operator++()
increment y first, then when reaching bottom, increment x
Definition display.cpp:599
const rect_of_hexes & rect_
Definition display.hpp:345
Rectangular area of hexes, allowing to decide how the top and bottom edges handles the vertical shift...
Definition display.hpp:320
iterator end() const
Definition display.cpp:616
iterator begin() const
Definition display.cpp:612
Type used to store color information of central and adjacent hexes.
Definition picture.hpp:124
Encapsulates the map of the game.
Definition location.hpp:46
static std::string write_direction(direction dir)
Definition location.cpp:154
bool valid() const
Definition location.hpp:111
direction
Valid directions which can be moved in our hexagonal world.
Definition location.hpp:48
void write(config &cfg) const
Definition location.cpp:223
std::string image
Definition overlay.hpp:55
std::string id
Definition overlay.hpp:59
std::string halo
Definition overlay.hpp:56
Holds a 2D point.
Definition point.hpp:25
An abstract description of a rectangle with integer coordinates.
Definition rect.hpp:49
void clip(const rect &r)
Clip this rectangle by the given rectangle.
Definition rect.cpp:101
constexpr point center() const
The center point of the rectangle, accounting for origin.
Definition rect.hpp:106
bool empty() const
False if both w and h are > 0, true otherwise.
Definition rect.cpp:49
constexpr point origin() const
Definition rect.hpp:66
bool contains(int x, int y) const
Whether the given point lies within the rectangle.
Definition rect.cpp:54
constexpr rect padded_by(int dx, int dy) const
Returns a new rectangle with dx horizontal padding and dy vertical padding.
Definition rect.hpp:158
rect intersect(const rect &r) const
Calculates the intersection of this rectangle and another; that is, the maximal rectangle that is con...
Definition rect.cpp:92
constexpr point size() const
Definition rect.hpp:67
void shift(const point &p)
Shift the rectangle by the given relative position.
Definition rect.cpp:106
bool overlaps(const rect &r) const
Whether the given rectangle and this rectangle overlap.
Definition rect.cpp:74
rect unsub_dest
Definition display.hpp:78
double size
Definition theme.hpp:90
std::string tile_image
Definition theme.hpp:93
std::string background_image
Definition theme.hpp:92
bool show_border
Definition theme.hpp:95
Object which defines a time of day with associated bonuses, image, sounds etc.
std::string id
tod_color color
The color modifications that should be made to the game board to reflect the time of day.
std::string image_mask
The image that is to be laid over all images while this time of day lasts.
Small struct to store and manipulate ToD color adjusts.
bool is_zero() const
mock_party p
static map_location::direction s
#define i
#define g
#define d
#define e
#define h
#define f
#define c
#define b