The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
frame.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2006 - 2025
3 by Jeremy Rosen <jeremy.rosen@enst-bretagne.fr>
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#include "units/frame.hpp"
17
18#include "color.hpp"
19#include "draw.hpp"
20#include "game_display.hpp"
21#include "log.hpp"
23#include "sound.hpp"
24
25static lg::log_domain log_engine("engine");
26#define ERR_NG LOG_STREAM(err, log_engine)
27
29 : duration_(1)
30 , auto_vflip_(boost::logic::indeterminate)
31 , auto_hflip_(boost::logic::indeterminate)
32 , primary_frame_(boost::logic::indeterminate)
33 , drawing_layer_(std::to_string(get_abs_frame_layer(drawing_layer::unit_default)))
34{}
35
36frame_builder::frame_builder(const config& cfg,const std::string& frame_string)
37 : duration_(1)
38 , image_(cfg[frame_string + "image"])
39 , image_diagonal_(cfg[frame_string + "image_diagonal"])
40 , image_mod_(cfg[frame_string + "image_mod"])
41 , halo_(cfg[frame_string + "halo"])
42 , halo_x_(cfg[frame_string + "halo_x"])
43 , halo_y_(cfg[frame_string + "halo_y"])
44 , halo_mod_(cfg[frame_string + "halo_mod"])
45 , sound_(cfg[frame_string + "sound"])
46 , text_(cfg[frame_string + "text"])
47 , blend_ratio_(cfg[frame_string + "blend_ratio"])
48 , highlight_ratio_(cfg[frame_string + "alpha"])
49 , offset_(cfg[frame_string + "offset"])
50 , submerge_(cfg[frame_string + "submerge"])
51 , x_(cfg[frame_string + "x"])
52 , y_(cfg[frame_string + "y"])
53 , directional_x_(cfg[frame_string + "directional_x"])
54 , directional_y_(cfg[frame_string + "directional_y"])
55 , auto_vflip_(boost::logic::indeterminate)
56 , auto_hflip_(boost::logic::indeterminate)
57 , primary_frame_(boost::logic::indeterminate)
58 , drawing_layer_(cfg[frame_string + "layer"])
59{
60 if(cfg.has_attribute(frame_string + "auto_vflip")) {
61 auto_vflip_ = cfg[frame_string + "auto_vflip"].to_bool();
62 }
63
64 if(cfg.has_attribute(frame_string + "auto_hflip")) {
65 auto_hflip_ = cfg[frame_string + "auto_hflip"].to_bool();
66 }
67
68 if(cfg.has_attribute(frame_string + "primary")) {
69 primary_frame_ = cfg[frame_string + "primary"].to_bool();
70 }
71
72 const auto& text_color_key = cfg[frame_string + "text_color"];
73 if(!text_color_key.empty()) {
74 try {
75 text_color_ = color_t::from_rgb_string(text_color_key.str());
76 } catch(const std::invalid_argument& e) {
77 // Might be thrown either due to an incorrect number of elements or std::stoul failure.
78 ERR_NG << "Invalid RBG text color in unit animation: " << text_color_key.str()
79 << "\n" << e.what();
80 }
81 }
82
83 if(const config::attribute_value* v = cfg.get(frame_string + "duration")) {
84 duration(chrono::parse_duration<std::chrono::milliseconds>(*v));
85 } else if(!cfg.get(frame_string + "end")) {
86 auto halo_duration = progressive_string(halo_, 1ms).duration();
87 auto image_duration = progressive_image(image_, 1ms).duration();
88 auto image_diagonal_duration = progressive_image(image_diagonal_, 1ms).duration();
89
90 duration(std::max(std::max(image_duration, image_diagonal_duration), halo_duration));
91 } else {
92 auto t1 = chrono::parse_duration<std::chrono::milliseconds>(cfg[frame_string + "begin"]);
93 auto t2 = chrono::parse_duration<std::chrono::milliseconds>(cfg[frame_string + "end"]);
94 duration(t2 - t1);
95 }
96
97 duration_ = std::max(duration_, 1ms);
98
99 const auto& blend_color_key = cfg[frame_string + "blend_color"];
100 if(!blend_color_key.empty()) {
101 try {
102 blend_with_ = color_t::from_rgb_string(blend_color_key.str());
103 } catch(const std::invalid_argument& e) {
104 // Might be thrown either due to an incorrect number of elements or std::stoul failure.
105 ERR_NG << "Invalid RBG blend color in unit animation: " << blend_color_key.str()
106 << "\n" << e.what();
107 }
108 }
109}
110
111frame_builder& frame_builder::image(const std::string& image ,const std::string& image_mod)
112{
113 image_ = image;
114 image_mod_ = image_mod;
115 return *this;
116}
117
118frame_builder& frame_builder::image_diagonal(const std::string& image_diagonal,const std::string& image_mod)
119{
121 image_mod_ = image_mod;
122 return *this;
123}
124
126{
127 sound_ = sound;
128 return *this;
129}
130
131frame_builder& frame_builder::text(const std::string& text,const color_t text_color)
132{
133 text_ = text;
134 text_color_ = text_color;
135 return *this;
136}
137
138frame_builder& frame_builder::halo(const std::string& halo, const std::string& halo_x, const std::string& halo_y,const std::string& halo_mod)
139{
140 halo_ = halo;
141 halo_x_ = halo_x;
142 halo_y_ = halo_y;
143 halo_mod_= halo_mod;
144 return *this;
145}
146
147frame_builder& frame_builder::duration(const std::chrono::milliseconds& duration)
148{
150 return *this;
151}
152
153frame_builder& frame_builder::blend(const std::string& blend_ratio,const color_t blend_color)
154{
155 blend_with_ = blend_color;
156 blend_ratio_ = blend_ratio;
157 return *this;
158}
159
160frame_builder& frame_builder::highlight(const std::string& highlight)
161{
163 return *this;
164}
165
166frame_builder& frame_builder::offset(const std::string& offset)
167{
168 offset_ = offset;
169 return *this;
170}
171
172frame_builder& frame_builder::submerge(const std::string& submerge)
173{
175 return *this;
176}
177
178frame_builder& frame_builder::x(const std::string& x)
179{
180 x_ = x;
181 return *this;
182}
183
184frame_builder& frame_builder::y(const std::string& y)
185{
186 y_ = y;
187 return *this;
188}
189
190frame_builder& frame_builder::directional_x(const std::string& directional_x)
191{
193 return *this;
194}
195
196frame_builder& frame_builder::directional_y(const std::string& directional_y)
197{
199 return *this;
200}
201
203{
205 return *this;
206}
207
209{
211 return *this;
212}
213
215{
217 return *this;
218}
219
221{
223 return *this;
224}
225
226frame_parsed_parameters::frame_parsed_parameters(const frame_builder& builder, const std::chrono::milliseconds& duration)
227 : duration_(duration > std::chrono::milliseconds{0} ? duration : builder.duration_)
228 , image_(builder.image_,duration_)
229 , image_diagonal_(builder.image_diagonal_,duration_)
230 , image_mod_(builder.image_mod_)
231 , halo_(builder.halo_,duration_)
232 , halo_x_(builder.halo_x_,duration_)
233 , halo_y_(builder.halo_y_,duration_)
234 , halo_mod_(builder.halo_mod_)
235 , sound_(builder.sound_)
236 , text_(builder.text_)
237 , text_color_(builder.text_color_)
238 , blend_with_(builder.blend_with_)
239 , blend_ratio_(builder.blend_ratio_,duration_)
240 , highlight_ratio_(builder.highlight_ratio_,duration_)
241 , offset_(builder.offset_,duration_)
242 , submerge_(builder.submerge_,duration_)
243 , x_(builder.x_,duration_)
244 , y_(builder.y_,duration_)
245 , directional_x_(builder.directional_x_,duration_)
246 , directional_y_(builder.directional_y_,duration_)
247 , auto_vflip_(builder.auto_vflip_)
248 , auto_hflip_(builder.auto_hflip_)
249 , primary_frame_(builder.primary_frame_)
250 , drawing_layer_(builder.drawing_layer_,duration_)
251{}
252
271
273{
274 return !this->does_not_change();
275}
276
277frame_parameters frame_parsed_parameters::parameters(const std::chrono::milliseconds& current_time) const
278{
279#ifdef __cpp_designated_initializers
280 return {
282 .image = image_.get_current_element(current_time),
283 .image_diagonal = image_diagonal_.get_current_element(current_time),
284 .image_mod = image_mod_,
285 .halo = halo_.get_current_element(current_time),
286 .halo_x = halo_x_.get_current_element(current_time),
287 .halo_y = halo_y_.get_current_element(current_time),
288 .halo_mod = halo_mod_,
289 .sound = sound_,
290 .text = text_,
291 .text_color = text_color_,
292 .blend_with = blend_with_,
293 .blend_ratio = blend_ratio_.get_current_element(current_time),
294 .highlight_ratio = highlight_ratio_.get_current_element(current_time,1.0),
295 .offset = offset_.get_current_element(current_time,-1000),
296 .submerge = submerge_.get_current_element(current_time),
297 .x = x_.get_current_element(current_time),
298 .y = y_.get_current_element(current_time),
299 .directional_x = directional_x_.get_current_element(current_time),
300 .directional_y = directional_y_.get_current_element(current_time),
301 .auto_vflip = auto_vflip_,
302 .auto_hflip = auto_hflip_,
303 .primary_frame = primary_frame_,
305 };
306#else
307 frame_parameters result;
308 result.duration = duration_;
309 result.image = image_.get_current_element(current_time);
311 result.image_mod = image_mod_;
312 result.halo = halo_.get_current_element(current_time);
313 result.halo_x = halo_x_.get_current_element(current_time);
314 result.halo_y = halo_y_.get_current_element(current_time);
315 result.halo_mod = halo_mod_;
316 result.sound = sound_;
317 result.text = text_;
318 result.text_color = text_color_;
319 result.blend_with = blend_with_;
320 result.blend_ratio = blend_ratio_.get_current_element(current_time);
321 result.highlight_ratio = highlight_ratio_.get_current_element(current_time,1.0);
322 result.offset = offset_.get_current_element(current_time,-1000);
323 result.submerge = submerge_.get_current_element(current_time);
324 result.x = x_.get_current_element(current_time);
325 result.y = y_.get_current_element(current_time);
326 result.directional_x = directional_x_.get_current_element(current_time);
327 result.directional_y = directional_y_.get_current_element(current_time);
328 result.auto_vflip = auto_vflip_;
329 result.auto_hflip = auto_hflip_;
332 return result;
333#endif
334}
335
336void frame_parsed_parameters::override(const std::chrono::milliseconds& duration,
337 const std::string& highlight,
338 const std::string& blend_ratio,
339 color_t blend_color,
340 const std::string& offset,
341 const std::string& layer,
342 const std::string& modifiers)
343{
344 if(!highlight.empty()) {
346 } else if(duration != duration_){
348 }
349
350 if(!offset.empty()) {
352 } else if(duration != duration_){
354 }
355
356 if(!blend_ratio.empty()) {
358 blend_with_ = blend_color;
359 } else if(duration != duration_){
361 }
362
363 if(!layer.empty()) {
365 } else if(duration != duration_){
367 }
368
369 if(!modifiers.empty()) {
370 image_mod_ += modifiers;
371 }
372
373 if(duration != duration_) {
385 }
386}
387
388std::vector<std::string> frame_parsed_parameters::debug_strings() const
389{
390 std::vector<std::string> v;
391
392 if(duration_ > 0ms) {
393 v.emplace_back("duration=" + utils::half_signed_value(duration_.count()));
394 }
395
396 if(!image_.get_original().empty()) {
397 v.emplace_back("image=" + image_.get_original());
398 }
399
400 if(!image_diagonal_.get_original().empty()) {
401 v.emplace_back("image_diagonal=" + image_diagonal_.get_original());
402 }
403
404 if(!image_mod_.empty()) {
405 v.emplace_back("image_mod=" + image_mod_);
406 }
407
408 if(!halo_.get_original().empty()) {
409 v.emplace_back("halo=" + halo_.get_original());
410 }
411
412 if(!halo_x_.get_original().empty()) {
413 v.emplace_back("halo_x=" + halo_x_.get_original());
414 }
415
416 if(!halo_y_.get_original().empty()) {
417 v.emplace_back("halo_y=" + halo_y_.get_original());
418 }
419
420 if(!halo_mod_.empty()) {
421 v.emplace_back("halo_mod=" + halo_mod_);
422 }
423
424 if(!sound_.empty()) {
425 v.emplace_back("sound=" + sound_);
426 }
427
428 if(!text_.empty()) {
429 v.emplace_back("text=" + text_);
430
431 if(text_color_) {
432 v.emplace_back("text_color=" + text_color_->to_rgba_string());
433 }
434 }
435
436 if(!blend_ratio_.get_original().empty()) {
437 v.emplace_back("blend_ratio=" + blend_ratio_.get_original());
438
439 if(blend_with_) {
440 v.emplace_back("blend_with=" + blend_with_->to_rgba_string());
441 }
442 }
443
444 if(!highlight_ratio_.get_original().empty()) {
445 v.emplace_back("highlight_ratio=" + highlight_ratio_.get_original());
446 }
447
448 if(!offset_.get_original().empty()) {
449 v.emplace_back("offset=" + offset_.get_original());
450 }
451
452 if(!submerge_.get_original().empty()) {
453 v.emplace_back("submerge=" + submerge_.get_original());
454 }
455
456 if(!x_.get_original().empty()) {
457 v.emplace_back("x=" + x_.get_original());
458 }
459
460 if(!y_.get_original().empty()) {
461 v.emplace_back("y=" + y_.get_original());
462 }
463
464 if(!directional_x_.get_original().empty()) {
465 v.emplace_back("directional_x=" + directional_x_.get_original());
466 }
467
468 if(!directional_y_.get_original().empty()) {
469 v.emplace_back("directional_y=" + directional_y_.get_original());
470 }
471
472 if(!boost::indeterminate(auto_vflip_)) {
473 v.emplace_back("auto_vflip=" + utils::bool_string(static_cast<bool>(auto_vflip_)));
474 }
475
476 if(!boost::indeterminate(auto_hflip_)) {
477 v.emplace_back("auto_hflip=" + utils::bool_string(static_cast<bool>(auto_hflip_)));
478 }
479
480 if(!boost::indeterminate(primary_frame_)) {
481 v.emplace_back("primary_frame=" + utils::bool_string(static_cast<bool>(primary_frame_)));
482 }
483
484 if(!drawing_layer_.get_original().empty()) {
485 v.emplace_back("drawing_layer=" + drawing_layer_.get_original());
486 }
487
488 return v;
489}
490
491namespace
492{
493void render_unit_image(
494 int x,
495 int y,
497 const map_location& loc,
498 const image::locator& i_locator,
499 bool hreverse,
500 uint8_t alpha,
501 double highlight,
502 color_t blendto,
503 double blend_ratio,
504 double submerge,
505 bool vreverse)
506{
507 const point image_size = image::get_size(i_locator);
508 if(!image_size.x || !image_size.y) {
509 return;
510 }
511
513
514 rect dest = disp->scaled_to_zoom({x, y, image_size.x, image_size.y});
515 if(!dest.overlaps(disp->map_area())) {
516 return;
517 }
518
519 texture tex = image::get_texture(i_locator);
520
521 // Clamp blend ratio so nothing weird happens
522 blend_ratio = std::clamp(blend_ratio, 0.0, 1.0);
523
524 submerge_data data = display::get_submerge_data(dest, submerge, image_size, alpha, hreverse, vreverse);
525
526 disp->drawing_buffer_add(drawing_layer, loc, [=](const rect&) mutable {
527 tex.set_alpha_mod(alpha);
528
529 if(submerge > 0.0) {
530 // set clip for dry part
531 // smooth_shaded doesn't use the clip information so it's fine to set it up front
532 tex.set_src(data.unsub_src);
533
534 // draw underwater part
535 draw::smooth_shaded(tex, data.alpha_verts);
536
537 // draw dry part
538 draw::flipped(tex, data.unsub_dest, hreverse, vreverse);
539 } else {
540 // draw whole texture
541 draw::flipped(tex, dest, hreverse, vreverse);
542 }
543
544 if(uint8_t hl = float_to_color(highlight); hl > 0) {
545 tex.set_blend_mode(SDL_BLENDMODE_ADD);
546 tex.set_alpha_mod(hl);
547
548 if(submerge > 0.0) {
549 // draw underwater part
550 draw::smooth_shaded(tex, data.alpha_verts);
551
552 // draw dry part
553 draw::flipped(tex, data.unsub_dest, hreverse, vreverse);
554 } else {
555 // draw whole texture
556 draw::flipped(tex, dest, hreverse, vreverse);
557 }
558 }
559
560 tex.set_blend_mode(SDL_BLENDMODE_BLEND);
561 tex.set_alpha_mod(SDL_ALPHA_OPAQUE);
562 });
563
564 // SDL hax to apply an active washout tint at the correct ratio
565 if(blend_ratio == 0.0) {
566 return;
567 }
568
569 // Get a pure-white version of the texture
570 const image::locator whiteout_locator(
571 i_locator.get_filename(),
572 i_locator.get_modifications()
573 + "~CHAN(255, 255, 255, alpha)"
574 );
575
576 disp->drawing_buffer_add(drawing_layer, loc, [=, tex = image::get_texture(whiteout_locator)](const rect&) mutable {
577 tex.set_alpha_mod(alpha * blend_ratio);
578 tex.set_color_mod(blendto);
579
580 if(submerge > 0.0) {
581 SDL_FColor fcolor;
582 fcolor.r = blendto.r;
583 fcolor.g = blendto.g;
584 fcolor.b = blendto.b;
585
586 // also draw submerged portion
587 // alpha_mod and color_mod are ignored,
588 // so we have to put them in the smooth shaded vertex data.
589 // This also has to incorporate the existing submerge alpha.
590 blendto.a = uint8_t(data.alpha_verts[0].color.a * blend_ratio);
591 data.alpha_verts[0].color = fcolor;
592 data.alpha_verts[1].color = fcolor;
593
594 blendto.a = uint8_t(data.alpha_verts[2].color.a * blend_ratio);
595 data.alpha_verts[2].color = fcolor;
596 data.alpha_verts[3].color = fcolor;
597
598 // set clip for dry part
599 // smooth_shaded doesn't use the clip information so it's fine to set it up front
600 tex.set_src(data.unsub_src);
601
602 // draw underwater part
603 draw::smooth_shaded(tex, data.alpha_verts);
604
605 // draw dry part
606 draw::flipped(tex, data.unsub_dest, hreverse, vreverse);
607 } else {
608 // draw whole texture
609 draw::flipped(tex, dest, hreverse, vreverse);
610 }
611
612 if(uint8_t hl = float_to_color(highlight); hl > 0) {
613 tex.set_blend_mode(SDL_BLENDMODE_ADD);
614 tex.set_alpha_mod(hl);
615
616 if(submerge > 0.0) {
617 // draw underwater part
618 draw::smooth_shaded(tex, data.alpha_verts);
619
620 // draw dry part
621 draw::flipped(tex, data.unsub_dest, hreverse, vreverse);
622 } else {
623 // draw whole texture
624 draw::flipped(tex, dest, hreverse, vreverse);
625 }
626 }
627
628 tex.set_color_mod(255, 255, 255);
629 tex.set_blend_mode(SDL_BLENDMODE_BLEND);
630 tex.set_alpha_mod(SDL_ALPHA_OPAQUE);
631 });
632}
633} // namespace
634
635void unit_frame::redraw(const std::chrono::milliseconds& frame_time, bool on_start_time, bool in_scope_of_frame,
636 const map_location& src, const map_location& dst,
637 halo::handle& halo_id, halo::manager& halo_man,
638 const frame_parameters& animation_val, const frame_parameters& engine_val) const
639{
641
642 const auto [xsrc, ysrc] = game_disp->get_location(src);
643 const auto [xdst, ydst] = game_disp->get_location(dst);
644 const map_location::direction direction = src.get_relative_dir(dst);
645
646 const frame_parameters current_data = merge_parameters(frame_time,animation_val,engine_val);
647 double tmp_offset = current_data.offset;
648
649 // Debug code to see the number of frames and their position
650 //if(tmp_offset) {
651 // std::cout << static_cast<int>(tmp_offset * 100) << "," << "\n";
652 //}
653
654 if(on_start_time) {
655 // Stuff that should be done only once per frame
656 if(!current_data.sound.empty() ) {
657 sound::play_sound(current_data.sound);
658 }
659
660 if(!current_data.text.empty() && current_data.text_color) {
661 game_disp->float_label(src, current_data.text, *current_data.text_color);
662 }
663 }
664
665 image::locator image_loc;
666 if(direction != map_location::direction::north && direction != map_location::direction::south) {
667 image_loc = current_data.image_diagonal.clone(current_data.image_mod);
668 }
669
670 if(image_loc.is_void() || image_loc.get_filename().empty()) { // invalid diag image, or not diagonal
671 image_loc = current_data.image.clone(current_data.image_mod);
672 }
673
674 point image_size {0, 0};
675 if(!image_loc.is_void() && !image_loc.get_filename().empty()) { // invalid diag image, or not diagonal
676 image_size = image::get_size(image_loc);
677 }
678
679 const int d2 = display::get_singleton()->hex_size() / 2;
680
681 const int x = static_cast<int>(tmp_offset * xdst + (1.0 - tmp_offset) * xsrc) + d2;
682 const int y = static_cast<int>(tmp_offset * ydst + (1.0 - tmp_offset) * ysrc) + d2;
683 const double disp_zoom = display::get_singleton()->get_zoom_factor();
684
685 if(image_size.x && image_size.y) {
686 bool facing_west = (
689
690 bool facing_north = (
692 direction == map_location::direction::north ||
694
695 if(!current_data.auto_hflip) { facing_west = false; }
696 if(!current_data.auto_vflip) { facing_north = true; }
697
698 int my_x = x + disp_zoom * (current_data.x - image_size.x / 2);
699 int my_y = y + disp_zoom * (current_data.y - image_size.y / 2);
700
701 if(facing_west) {
702 my_x -= current_data.directional_x * disp_zoom;
703 } else {
704 my_x += current_data.directional_x * disp_zoom;
705 }
706
707 if(facing_north) {
708 my_y += current_data.directional_y * disp_zoom;
709 } else {
710 my_y -= current_data.directional_y * disp_zoom;
711 }
712
713 // TODO: don't conflate highlights and alpha
714 double brighten;
715 uint8_t alpha;
716 if(current_data.highlight_ratio >= 1.0) {
717 brighten = current_data.highlight_ratio - 1.0;
718 alpha = 255;
719 } else {
720 brighten = 0.0;
721 alpha = float_to_color(current_data.highlight_ratio);
722 }
723
724 if(alpha != 0) {
725 render_unit_image(my_x, my_y,
727 src,
728 image_loc,
729 facing_west,
730 alpha,
731 brighten,
732 current_data.blend_with ? *current_data.blend_with : color_t(),
733 current_data.blend_ratio,
734 current_data.submerge,
735 !facing_north
736 );
737 }
738 }
739
740 halo_id.reset();
741
742 if(!in_scope_of_frame) { //check after frame as first/last frame image used in defense/attack anims
743 return;
744 }
745
746 // No halos, exit
747 if(current_data.halo.empty()) {
748 return;
749 }
750
751 halo::ORIENTATION orientation;
752 switch(direction)
753 {
756 orientation = halo::NORMAL;
757 break;
760 if(!current_data.auto_vflip) {
761 orientation = halo::NORMAL;
762 } else {
763 orientation = halo::VREVERSE;
764 }
765 break;
767 if(!current_data.auto_vflip) {
768 orientation = halo::HREVERSE;
769 } else {
770 orientation = halo::HVREVERSE;
771 }
772 break;
774 orientation = halo::HREVERSE;
775 break;
777 default:
778 orientation = halo::NORMAL;
779 break;
780 }
781
783 halo_id = halo_man.add(
784 static_cast<int>(x + current_data.halo_x * disp_zoom),
785 static_cast<int>(y + current_data.halo_y * disp_zoom),
786 current_data.halo + current_data.halo_mod,
787 map_location(-1, -1),
788 orientation
789 );
790 } else {
791 halo_id = halo_man.add(
792 static_cast<int>(x - current_data.halo_x * disp_zoom),
793 static_cast<int>(y + current_data.halo_y * disp_zoom),
794 current_data.halo + current_data.halo_mod,
795 map_location(-1, -1),
796 orientation
797 );
798 }
799}
800
801std::set<map_location> unit_frame::get_overlaped_hex(const std::chrono::milliseconds& frame_time, const map_location& src, const map_location& dst,
802 const frame_parameters& animation_val, const frame_parameters& engine_val) const
803{
805
806 const auto [xsrc, ysrc] = disp->get_location(src);
807 const auto [xdst, ydst] = disp->get_location(dst);
808 const map_location::direction direction = src.get_relative_dir(dst);
809
810 const frame_parameters current_data = merge_parameters(frame_time, animation_val, engine_val);
811
812 double tmp_offset = current_data.offset;
813 const int d2 = display::get_singleton()->hex_size() / 2;
814
815 image::locator image_loc;
816 if(direction != map_location::direction::north && direction != map_location::direction::south) {
817 image_loc = current_data.image_diagonal.clone(current_data.image_mod);
818 }
819
820 if(image_loc.is_void() || image_loc.get_filename().empty()) { // invalid diag image, or not diagonal
821 image_loc = current_data.image.clone(current_data.image_mod);
822 }
823
824 // We always invalidate our own hex because we need to be called at redraw time even
825 // if we don't draw anything in the hex itself
826 std::set<map_location> result;
827 if(tmp_offset == 0 && current_data.x == 0 && current_data.directional_x == 0 && image::is_in_hex(image_loc)) {
828 result.insert(src);
829
830 bool facing_north = (
832 direction == map_location::direction::north ||
834
835 if(!current_data.auto_vflip) { facing_north = true; }
836
837 int my_y = current_data.y;
838 if(facing_north) {
839 my_y += current_data.directional_y;
840 } else {
841 my_y -= current_data.directional_y;
842 }
843
844 if(my_y < 0) {
845 result.insert(src.get_direction(map_location::direction::north));
846 result.insert(src.get_direction(map_location::direction::north_east));
847 result.insert(src.get_direction(map_location::direction::north_west));
848 } else if(my_y > 0) {
849 result.insert(src.get_direction(map_location::direction::south));
850 result.insert(src.get_direction(map_location::direction::south_east));
851 result.insert(src.get_direction(map_location::direction::south_west));
852 }
853 } else {
854 int w = 0, h = 0;
855
856 if(!image_loc.is_void() && !image_loc.get_filename().empty()) {
857 const point s = image::get_size(image_loc);
858 w = s.x;
859 h = s.y;
860 }
861
862 if(w != 0 || h != 0) {
863 // TODO: unduplicate this code
864 const int x = static_cast<int>(tmp_offset * xdst + (1.0 - tmp_offset) * xsrc) + d2;
865 const int y = static_cast<int>(tmp_offset * ydst + (1.0 - tmp_offset) * ysrc) + d2;
866 const double disp_zoom = display::get_singleton()->get_zoom_factor();
867
868 bool facing_west = (
871
872 bool facing_north = (
874 direction == map_location::direction::north ||
876
877 if(!current_data.auto_hflip) { facing_west = false; }
878 if(!current_data.auto_vflip) { facing_north = true; }
879
880 int my_x = x + disp_zoom * (current_data.x - w / 2);
881 int my_y = y + disp_zoom * (current_data.y - h / 2);
882
883 if(facing_west) {
884 my_x -= current_data.directional_x * disp_zoom;
885 } else {
886 my_x += current_data.directional_x * disp_zoom;
887 }
888
889 if(facing_north) {
890 my_y += current_data.directional_y * disp_zoom;
891 } else {
892 my_y -= current_data.directional_y * disp_zoom;
893 }
894
895 // Check if our underlying hexes are invalidated. If we need to update ourselves because we changed,
896 // invalidate our hexes and return whether or not was successful.
897 const rect r {my_x, my_y, int(w * disp_zoom), int(h * disp_zoom)};
898 display::rect_of_hexes underlying_hex = disp->hexes_under_rect(r);
899
900 result.insert(src);
901 result.insert(underlying_hex.begin(), underlying_hex.end());
902 } else {
903 // We have no "redraw surface" but we still need to invalidate our own hex in case we have a halo
904 // and/or sound that needs a redraw.
905 result.insert(src);
906 result.insert(dst);
907 }
908 }
909
910 return result;
911}
912
913/**
914 * This function merges the value provided by:
915 * - the frame
916 * - the engine (poison, flying unit...)
917 * - the animation as a whole
918 *
919 * There is no absolute rule for merging, so creativity is the rule. If a value is never provided by the engine, assert.
920 * This way if it becomes used, people will easily find the right place to look.
921 */
922frame_parameters unit_frame::merge_parameters(const std::chrono::milliseconds& current_time,
923 const frame_parameters& animation_val,
924 const frame_parameters& engine_val) const
925{
926 frame_parameters result;
927 const frame_parameters& current_val = builder_.parameters(current_time);
928
929 result.primary_frame = engine_val.primary_frame;
930 if(!boost::logic::indeterminate(animation_val.primary_frame)) {
931 result.primary_frame = animation_val.primary_frame;
932 }
933
934 if(!boost::logic::indeterminate(current_val.primary_frame)) {
935 result.primary_frame = current_val.primary_frame;
936 }
937
938 // Convert the tribool to bool
939 const bool primary = static_cast<bool>(result.primary_frame) || boost::logic::indeterminate(result.primary_frame);
940
941 /** The engine provides a default image to use for the unit when none is available */
942 result.image = current_val.image.is_void() || current_val.image.get_filename().empty()
943 ? animation_val.image
944 : current_val.image;
945
946 if(primary && (result.image.is_void() || result.image.get_filename().empty())) {
947 result.image = engine_val.image;
948 }
949
950 /** The engine provides a default image to use for the unit when none is available */
951 result.image_diagonal = current_val.image_diagonal.is_void() || current_val.image_diagonal.get_filename().empty()
952 ? animation_val.image_diagonal
953 : current_val.image_diagonal;
954
955 if(primary && (result.image_diagonal.is_void() || result.image_diagonal.get_filename().empty())) {
956 result.image_diagonal = engine_val.image_diagonal;
957 }
958
959 /**
960 * The engine provides a string for "petrified" and "team color" modifications.
961 * Note that image_mod is the complete modification and halo_mod is only the TC part.
962 */
963 result.image_mod = current_val.image_mod + animation_val.image_mod;
964 if(primary) {
965 result.image_mod += engine_val.image_mod;
966 } else {
967 result.image_mod += engine_val.halo_mod;
968 }
969
970 assert(engine_val.halo.empty());
971 result.halo = current_val.halo.empty() ? animation_val.halo : current_val.halo;
972
973 assert(engine_val.halo_x == 0);
974 result.halo_x = current_val.halo_x ? current_val.halo_x : animation_val.halo_x;
975
976 /** The engine provides a y modification for terrain with height adjust and flying units */
977 result.halo_y = current_val.halo_y ? current_val.halo_y : animation_val.halo_y;
978 result.halo_y += engine_val.halo_y;
979
980 result.halo_mod = current_val.halo_mod + animation_val.halo_mod;
981 result.halo_mod += engine_val.halo_mod;
982
983 assert(engine_val.duration == 0ms);
984 result.duration = current_val.duration;
985
986 assert(engine_val.sound.empty());
987 result.sound = current_val.sound.empty() ? animation_val.sound : current_val.sound;
988
989 assert(engine_val.text.empty());
990 result.text = current_val.text.empty() ? animation_val.text : current_val.text;
991
992 assert(!engine_val.text_color);
993 result.text_color = current_val.text_color ? current_val.text_color : animation_val.text_color;
994
995 /** The engine provides a blend color for poisoned units */
996 result.blend_with = current_val.blend_with ? current_val.blend_with : animation_val.blend_with;
997 if(primary && engine_val.blend_with) {
998 result.blend_with = engine_val.blend_with->blend_lighten(result.blend_with ? *result.blend_with : color_t(0,0,0));
999 }
1000
1001 /** The engine provides a blend color for poisoned units */
1002 result.blend_ratio = current_val.blend_ratio != 0 ? current_val.blend_ratio:animation_val.blend_ratio;
1003 if(primary && engine_val.blend_ratio != 0) {
1004 result.blend_ratio = std::min(result.blend_ratio + engine_val.blend_ratio, 1.0);
1005 }
1006
1007 /** The engine provides a highlight ratio for selected units and visible "invisible" units */
1008 result.highlight_ratio = (current_val.highlight_ratio < 0.999 || current_val.highlight_ratio > 1.001) ?
1009 current_val.highlight_ratio : animation_val.highlight_ratio;
1010 if(primary && (engine_val.highlight_ratio < 0.999 || engine_val.highlight_ratio > 1.001)) {
1011 result.highlight_ratio = result.highlight_ratio * engine_val.highlight_ratio; // selected unit
1012 }
1013
1014 assert(engine_val.offset == 0);
1015 result.offset = (current_val.offset != -1000) ? current_val.offset : animation_val.offset;
1016 if(result.offset == -1000) {
1017 result.offset = 0.0;
1018 }
1019
1020 /** The engine provides a submerge for units in water */
1021 result.submerge = current_val.submerge != 0 ? current_val.submerge : animation_val.submerge;
1022 if(primary && engine_val.submerge != 0 && result.submerge == 0) {
1023 result.submerge = engine_val.submerge;
1024 }
1025
1026 assert(engine_val.x == 0);
1027 result.x = current_val.x ? current_val.x : animation_val.x;
1028
1029 /** The engine provides a y modification for terrain with height adjust and flying units */
1030 result.y = current_val.y?current_val.y:animation_val.y;
1031 result.y += engine_val.y;
1032
1033 assert(engine_val.directional_x == 0);
1034 result.directional_x = current_val.directional_x ? current_val.directional_x : animation_val.directional_x;
1035
1036 assert(engine_val.directional_y == 0);
1037 result.directional_y = current_val.directional_y ? current_val.directional_y : animation_val.directional_y;
1038
1041 ? current_val.drawing_layer
1042 : animation_val.drawing_layer;
1043
1044 /** The engine provides us with a default value to compare to. Update if different */
1045 result.auto_hflip = engine_val.auto_hflip;
1046
1047 if(!boost::logic::indeterminate(animation_val.auto_hflip)) {
1048 result.auto_hflip = animation_val.auto_hflip;
1049 }
1050
1051 if(!boost::logic::indeterminate(current_val.auto_hflip)) {
1052 result.auto_hflip = current_val.auto_hflip;
1053 }
1054
1055 if(boost::logic::indeterminate(result.auto_hflip)) {
1056 result.auto_hflip = true;
1057 }
1058
1059 result.auto_vflip = engine_val.auto_vflip;
1060
1061 if(!boost::logic::indeterminate(animation_val.auto_vflip)) {
1062 result.auto_vflip = animation_val.auto_vflip;
1063 }
1064
1065 if(!boost::logic::indeterminate(current_val.auto_vflip)) {
1066 result.auto_vflip = current_val.auto_vflip;
1067 }
1068
1069 if(boost::logic::indeterminate(result.auto_vflip)) {
1070 result.auto_vflip = !primary;
1071 }
1072
1073 return result;
1074}
map_location loc
Definition move.cpp:172
#define ERR_NG
Variant for storing WML attributes.
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
const attribute_value * get(std::string_view key) const
Returns a pointer to the attribute with the given key or nullptr if it does not exist.
Definition config.cpp:665
bool has_attribute(std::string_view key) const
Definition config.cpp:157
Sort-of-Singleton that many classes, both GUI and non-GUI, use to access the game data.
Definition display.hpp:88
point get_location(const map_location &loc) const
Functions to get the on-screen positions of hexes.
Definition display.cpp:674
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
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
static rect scaled_to_zoom(const rect &r)
Scale the width and height of a rect by the current zoom factor.
Definition display.hpp:268
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
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
rect map_area() const
Returns the area used for the map.
Definition display.cpp:490
static display * get_singleton()
Returns the display object if a display object exists.
Definition display.hpp:102
Easily build frame parameters with the serialized constructors.
Definition frame.hpp:84
std::string offset_
Definition frame.hpp:129
std::string halo_y_
Definition frame.hpp:119
std::string image_mod_
Definition frame.hpp:116
utils::optional< color_t > blend_with_
Definition frame.hpp:125
std::string halo_mod_
Definition frame.hpp:120
std::string image_diagonal_
Definition frame.hpp:115
boost::tribool auto_vflip_
Definition frame.hpp:136
boost::tribool primary_frame_
Definition frame.hpp:138
frame_builder & auto_hflip(const bool auto_hflip)
Definition frame.cpp:208
std::string halo_
Definition frame.hpp:117
frame_builder & highlight(const std::string &highlight)
Definition frame.cpp:160
std::string sound_
Definition frame.hpp:121
frame_builder & image_diagonal(const std::string &image_diagonal, const std::string &image_mod="")
Definition frame.cpp:118
frame_builder & directional_x(const std::string &directional_x)
Definition frame.cpp:190
frame_builder & submerge(const std::string &submerge)
Definition frame.cpp:172
std::string image_
Definition frame.hpp:114
std::string submerge_
Definition frame.hpp:130
frame_builder & y(const std::string &y)
Definition frame.cpp:184
frame_builder & primary_frame(const bool primary_frame)
Definition frame.cpp:214
frame_builder & auto_vflip(const bool auto_vflip)
Definition frame.cpp:202
std::string y_
Definition frame.hpp:132
std::string highlight_ratio_
Definition frame.hpp:128
boost::tribool auto_hflip_
Definition frame.hpp:137
std::string blend_ratio_
Definition frame.hpp:127
std::string text_
Definition frame.hpp:122
std::string drawing_layer_
Definition frame.hpp:140
frame_builder & drawing_layer(const std::string &drawing_layer)
Definition frame.cpp:220
std::string directional_x_
Definition frame.hpp:133
std::string directional_y_
Definition frame.hpp:134
std::string halo_x_
Definition frame.hpp:118
frame_builder & text(const std::string &text, const color_t text_color)
Definition frame.cpp:131
frame_builder & blend(const std::string &blend_ratio, const color_t blend_color)
Definition frame.cpp:153
frame_builder & x(const std::string &x)
Definition frame.cpp:178
utils::optional< color_t > text_color_
Definition frame.hpp:124
frame_builder & offset(const std::string &offset)
Definition frame.cpp:166
frame_builder & directional_y(const std::string &directional_y)
Definition frame.cpp:196
frame_builder & image(const std::string &image, const std::string &image_mod="")
Definition frame.cpp:111
frame_builder & sound(const std::string &sound)
Definition frame.cpp:125
std::string x_
Definition frame.hpp:131
frame_builder & duration(const std::chrono::milliseconds &duration)
Allow easy chained modifications.
Definition frame.cpp:147
frame_builder & halo(const std::string &halo, const std::string &halo_x, const std::string &halo_y, const std::string &halo_mod)
Definition frame.cpp:138
std::chrono::milliseconds duration_
Definition frame.hpp:112
void override(const std::chrono::milliseconds &duration, const std::string &highlight="", const std::string &blend_ratio="", color_t blend_color={0, 0, 0}, const std::string &offset="", const std::string &layer="", const std::string &modifiers="")
Definition frame.cpp:336
progressive_int directional_x_
Definition frame.hpp:196
progressive_int halo_y_
Definition frame.hpp:181
boost::tribool auto_hflip_
Definition frame.hpp:200
const std::chrono::milliseconds & duration() const
Definition frame.hpp:164
progressive_image image_
Definition frame.hpp:174
boost::tribool primary_frame_
Definition frame.hpp:201
bool need_update() const
Definition frame.cpp:272
progressive_double blend_ratio_
Definition frame.hpp:190
progressive_double highlight_ratio_
Definition frame.hpp:191
progressive_double offset_
Definition frame.hpp:192
boost::tribool auto_vflip_
Definition frame.hpp:199
progressive_string halo_
Definition frame.hpp:179
frame_parameters parameters(const std::chrono::milliseconds &current_time) const
Getters for the different parameters.
Definition frame.cpp:277
utils::optional< color_t > blend_with_
Definition frame.hpp:188
progressive_int x_
Definition frame.hpp:194
std::chrono::milliseconds duration_
Definition frame.hpp:172
progressive_double submerge_
Definition frame.hpp:193
std::string halo_mod_
Definition frame.hpp:183
std::string image_mod_
Definition frame.hpp:177
progressive_int halo_x_
Definition frame.hpp:180
progressive_int y_
Definition frame.hpp:195
progressive_int drawing_layer_
Definition frame.hpp:203
utils::optional< color_t > text_color_
Definition frame.hpp:187
std::vector< std::string > debug_strings() const
Contents of frame in strings.
Definition frame.cpp:388
progressive_int directional_y_
Definition frame.hpp:197
progressive_image image_diagonal_
Definition frame.hpp:175
frame_parsed_parameters(const frame_builder &builder=frame_builder(), const std::chrono::milliseconds &override_duration=std::chrono::milliseconds{0})
Definition frame.cpp:226
bool does_not_change() const
Definition frame.cpp:253
static game_display * get_singleton()
void float_label(const map_location &loc, const std::string &text, const color_t &color)
Function to float a label above a tile.
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
Generic locator abstracting the location of an image.
Definition picture.hpp:59
bool is_void() const
Returns true if the locator does not correspond to an actual image.
Definition picture.hpp:93
const std::string & get_filename() const
Definition picture.hpp:82
locator clone(const std::string &mods) const
Returns a copy of this locator with the given IPF.
Definition picture.cpp:228
const std::string & get_modifications() const
Definition picture.hpp:87
std::string get_original() const
virtual bool does_not_change() const
std::chrono::milliseconds duration() const
virtual const T get_current_element(const std::chrono::milliseconds &current_time, T default_val=T()) const override
bool does_not_change() const override
virtual const T get_current_element(const std::chrono::milliseconds &current_time, T default_val=T()) const override
Wrapper class to encapsulate creation and management of an SDL_Texture.
Definition texture.hpp:33
void set_blend_mode(SDL_BlendMode mode)
Blend mode.
Definition texture.cpp:185
void set_src(const rect &r)
Set the source region of the texture used for drawing operations.
Definition texture.cpp:122
void set_alpha_mod(uint8_t alpha)
Alpha modifier.
Definition texture.cpp:146
void set_color_mod(uint8_t r, uint8_t g, uint8_t b)
Colour modifier.
Definition texture.cpp:168
void redraw(const std::chrono::milliseconds &frame_time, bool on_start_time, bool in_scope_of_frame, const map_location &src, const map_location &dst, halo::handle &halo_id, halo::manager &halo_man, const frame_parameters &animation_val, const frame_parameters &engine_val) const
Definition frame.cpp:635
std::set< map_location > get_overlaped_hex(const std::chrono::milliseconds &frame_time, const map_location &src, const map_location &dst, const frame_parameters &animation_val, const frame_parameters &engine_val) const
Definition frame.cpp:801
frame_parameters merge_parameters(const std::chrono::milliseconds &current_time, const frame_parameters &animation_val, const frame_parameters &engine_val=frame_parameters()) const
This function merges the value provided by:
Definition frame.cpp:922
frame_parsed_parameters builder_
Definition frame.hpp:255
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
Drawing functions, for drawing things on the screen.
drawing_layer
@ unit_default
Default layer for drawing units.
@ unit_first
Reserve layers to be selected for wml.
const config * cfg
static lg::log_domain log_engine("engine")
Frame for unit's animation sequence.
constexpr int get_abs_frame_layer(drawing_layer layer)
Definition frame.hpp:37
progressive_pair< double > progressive_double
progressive_single< image::locator > progressive_image
progressive_single< std::string > progressive_string
progressive_pair< int > progressive_int
Standard logging facilities (interface).
Definition span.hpp:53
void flipped(const texture &tex, const ::rect &dst, bool flip_h=true, bool flip_v=false)
Draws a texture, or part of a texture, at the given location, also mirroring/flipping the texture hor...
Definition draw.cpp:452
void smooth_shaded(const texture &tex, const std::array< SDL_Vertex, 4 > &verts)
Draw a texture with smoothly varying colour and alpha modification, specified at the four corners of ...
Definition draw.cpp:556
Definition halo.cpp:41
ORIENTATION
Definition halo.hpp:35
@ HVREVERSE
Definition halo.hpp:35
@ VREVERSE
Definition halo.hpp:35
@ HREVERSE
Definition halo.hpp:35
@ NORMAL
Definition halo.hpp:35
std::shared_ptr< halo_record > handle
Definition halo.hpp:33
Functions to load and save images from/to disk.
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
point get_size(const locator &i_locator, bool skip_cache)
Returns the width and height of an image.
Definition picture.cpp:815
bool is_in_hex(const locator &i_locator)
Checks if an image fits into a single hex.
Definition picture.cpp:824
Audio output for sound and music.
void play_sound(const std::string &files, sound_tracks::type group, unsigned int repeats)
Definition sound.cpp:978
STL namespace.
std::string half_signed_value(int val)
Sign with Unicode "−" if negative.
std::string bool_string(const bool value)
Converts a bool value to 'true' or 'false'.
int w
Definition pathfind.cpp:188
std::string_view data
Definition picture.cpp:188
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
static color_t from_rgb_string(std::string_view c)
Creates a new opaque color_t object from a string variable in "R,G,B" format.
Definition color.cpp:44
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
All parameters from a frame at a given instant.
Definition frame.hpp:44
std::string halo
Definition frame.hpp:51
utils::optional< color_t > blend_with
Definition frame.hpp:61
double highlight_ratio
Definition frame.hpp:64
std::string text
Definition frame.hpp:58
boost::tribool auto_hflip
Definition frame.hpp:74
double offset
Definition frame.hpp:65
std::string sound
Definition frame.hpp:57
std::string image_mod
Definition frame.hpp:50
double submerge
Definition frame.hpp:66
image::locator image_diagonal
Definition frame.hpp:48
boost::tribool primary_frame
Definition frame.hpp:75
image::locator image
Definition frame.hpp:47
std::chrono::milliseconds duration
Definition frame.hpp:45
utils::optional< color_t > text_color
Definition frame.hpp:60
double blend_ratio
Definition frame.hpp:63
std::string halo_mod
Definition frame.hpp:56
boost::tribool auto_vflip
Definition frame.hpp:73
Encapsulates the map of the game.
Definition location.hpp:46
direction
Valid directions which can be moved in our hexagonal world.
Definition location.hpp:48
Holds a 2D point.
Definition point.hpp:25
An abstract description of a rectangle with integer coordinates.
Definition rect.hpp:49
bool overlaps(const rect &r) const
Whether the given rectangle and this rectangle overlap.
Definition rect.cpp:74
static map_location::direction s
#define e
#define h