The Battle for Wesnoth  1.19.7+dev
frame.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2024
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"
22 #include "serialization/chrono.hpp"
23 #include "sound.hpp"
24 
25 static 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 
36 frame_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 
111 frame_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 
118 frame_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 
131 frame_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 
138 frame_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 
147 frame_builder& frame_builder::duration(const std::chrono::milliseconds& duration)
148 {
150  return *this;
151 }
152 
153 frame_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 
160 frame_builder& frame_builder::highlight(const std::string& highlight)
161 {
163  return *this;
164 }
165 
166 frame_builder& frame_builder::offset(const std::string& offset)
167 {
168  offset_ = offset;
169  return *this;
170 }
171 
172 frame_builder& frame_builder::submerge(const std::string& submerge)
173 {
175  return *this;
176 }
177 
178 frame_builder& frame_builder::x(const std::string& x)
179 {
180  x_ = x;
181  return *this;
182 }
183 
184 frame_builder& frame_builder::y(const std::string& y)
185 {
186  y_ = y;
187  return *this;
188 }
189 
190 frame_builder& frame_builder::directional_x(const std::string& directional_x)
191 {
193  return *this;
194 }
195 
196 frame_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 
214 frame_builder& frame_builder::primary_frame(const bool primary_frame)
215 {
217  return *this;
218 }
219 
221 {
223  return *this;
224 }
225 
226 frame_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 
254 {
255  return
265  x_.does_not_change() &&
266  y_.does_not_change() &&
270 }
271 
273 {
274  return !this->does_not_change();
275 }
276 
277 frame_parameters frame_parsed_parameters::parameters(const std::chrono::milliseconds& current_time) const
278 {
279 #ifdef __cpp_designated_initializers
280  return {
281  .duration = duration_,
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);
310  result.image_diagonal = image_diagonal_.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_;
330  result.primary_frame = primary_frame_;
332  return result;
333 #endif
334 }
335 
336 void 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 
388 std::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 
491 namespace
492 {
493 void 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  // also draw submerged portion
582  // alpha_mod and color_mod are ignored,
583  // so we have to put them in the smooth shaded vertex data.
584  // This also has to incorporate the existing submerge alpha.
585  blendto.a = uint8_t(data.alpha_verts[0].color.a * blend_ratio);
586  data.alpha_verts[0].color = blendto;
587  data.alpha_verts[1].color = blendto;
588 
589  blendto.a = uint8_t(data.alpha_verts[2].color.a * blend_ratio);
590  data.alpha_verts[2].color = blendto;
591  data.alpha_verts[3].color = blendto;
592 
593  // set clip for dry part
594  // smooth_shaded doesn't use the clip information so it's fine to set it up front
595  tex.set_src(data.unsub_src);
596 
597  // draw underwater part
598  draw::smooth_shaded(tex, data.alpha_verts);
599 
600  // draw dry part
601  draw::flipped(tex, data.unsub_dest, hreverse, vreverse);
602  } else {
603  // draw whole texture
604  draw::flipped(tex, dest, hreverse, vreverse);
605  }
606 
607  if(uint8_t hl = float_to_color(highlight); hl > 0) {
608  tex.set_blend_mode(SDL_BLENDMODE_ADD);
609  tex.set_alpha_mod(hl);
610 
611  if(submerge > 0.0) {
612  // draw underwater part
613  draw::smooth_shaded(tex, data.alpha_verts);
614 
615  // draw dry part
616  draw::flipped(tex, data.unsub_dest, hreverse, vreverse);
617  } else {
618  // draw whole texture
619  draw::flipped(tex, dest, hreverse, vreverse);
620  }
621  }
622 
623  tex.set_color_mod(255, 255, 255);
624  tex.set_blend_mode(SDL_BLENDMODE_BLEND);
625  tex.set_alpha_mod(SDL_ALPHA_OPAQUE);
626  });
627 }
628 } // namespace
629 
630 void unit_frame::redraw(const std::chrono::milliseconds& frame_time, bool on_start_time, bool in_scope_of_frame,
631  const map_location& src, const map_location& dst,
632  halo::handle& halo_id, halo::manager& halo_man,
633  const frame_parameters& animation_val, const frame_parameters& engine_val) const
634 {
636 
637  const auto [xsrc, ysrc] = game_disp->get_location(src);
638  const auto [xdst, ydst] = game_disp->get_location(dst);
639  const map_location::direction direction = src.get_relative_dir(dst);
640 
641  const frame_parameters current_data = merge_parameters(frame_time,animation_val,engine_val);
642  double tmp_offset = current_data.offset;
643 
644  // Debug code to see the number of frames and their position
645  //if(tmp_offset) {
646  // std::cout << static_cast<int>(tmp_offset * 100) << "," << "\n";
647  //}
648 
649  if(on_start_time) {
650  // Stuff that should be done only once per frame
651  if(!current_data.sound.empty() ) {
652  sound::play_sound(current_data.sound);
653  }
654 
655  if(!current_data.text.empty() && current_data.text_color) {
656  game_disp->float_label(src, current_data.text, *current_data.text_color);
657  }
658  }
659 
660  image::locator image_loc;
661  if(direction != map_location::direction::north && direction != map_location::direction::south) {
662  image_loc = current_data.image_diagonal.clone(current_data.image_mod);
663  }
664 
665  if(image_loc.is_void() || image_loc.get_filename().empty()) { // invalid diag image, or not diagonal
666  image_loc = current_data.image.clone(current_data.image_mod);
667  }
668 
669  point image_size {0, 0};
670  if(!image_loc.is_void() && !image_loc.get_filename().empty()) { // invalid diag image, or not diagonal
671  image_size = image::get_size(image_loc);
672  }
673 
674  const int d2 = display::get_singleton()->hex_size() / 2;
675 
676  const int x = static_cast<int>(tmp_offset * xdst + (1.0 - tmp_offset) * xsrc) + d2;
677  const int y = static_cast<int>(tmp_offset * ydst + (1.0 - tmp_offset) * ysrc) + d2;
678  const double disp_zoom = display::get_singleton()->get_zoom_factor();
679 
680  if(image_size.x && image_size.y) {
681  bool facing_west = (
684 
685  bool facing_north = (
687  direction == map_location::direction::north ||
689 
690  if(!current_data.auto_hflip) { facing_west = false; }
691  if(!current_data.auto_vflip) { facing_north = true; }
692 
693  int my_x = x + disp_zoom * (current_data.x - image_size.x / 2);
694  int my_y = y + disp_zoom * (current_data.y - image_size.y / 2);
695 
696  if(facing_west) {
697  my_x -= current_data.directional_x * disp_zoom;
698  } else {
699  my_x += current_data.directional_x * disp_zoom;
700  }
701 
702  if(facing_north) {
703  my_y += current_data.directional_y * disp_zoom;
704  } else {
705  my_y -= current_data.directional_y * disp_zoom;
706  }
707 
708  // TODO: don't conflate highlights and alpha
709  double brighten;
710  uint8_t alpha;
711  if(current_data.highlight_ratio >= 1.0) {
712  brighten = current_data.highlight_ratio - 1.0;
713  alpha = 255;
714  } else {
715  brighten = 0.0;
716  alpha = float_to_color(current_data.highlight_ratio);
717  }
718 
719  if(alpha != 0) {
720  render_unit_image(my_x, my_y,
721  drawing_layer { int(drawing_layer::unit_first) + current_data.drawing_layer },
722  src,
723  image_loc,
724  facing_west,
725  alpha,
726  brighten,
727  current_data.blend_with ? *current_data.blend_with : color_t(),
728  current_data.blend_ratio,
729  current_data.submerge,
730  !facing_north
731  );
732  }
733  }
734 
735  halo_id.reset();
736 
737  if(!in_scope_of_frame) { //check after frame as first/last frame image used in defense/attack anims
738  return;
739  }
740 
741  // No halos, exit
742  if(current_data.halo.empty()) {
743  return;
744  }
745 
746  halo::ORIENTATION orientation;
747  switch(direction)
748  {
751  orientation = halo::NORMAL;
752  break;
755  if(!current_data.auto_vflip) {
756  orientation = halo::NORMAL;
757  } else {
758  orientation = halo::VREVERSE;
759  }
760  break;
762  if(!current_data.auto_vflip) {
763  orientation = halo::HREVERSE;
764  } else {
765  orientation = halo::HVREVERSE;
766  }
767  break;
769  orientation = halo::HREVERSE;
770  break;
772  default:
773  orientation = halo::NORMAL;
774  break;
775  }
776 
778  halo_id = halo_man.add(
779  static_cast<int>(x + current_data.halo_x * disp_zoom),
780  static_cast<int>(y + current_data.halo_y * disp_zoom),
781  current_data.halo + current_data.halo_mod,
782  map_location(-1, -1),
783  orientation
784  );
785  } else {
786  halo_id = halo_man.add(
787  static_cast<int>(x - current_data.halo_x * disp_zoom),
788  static_cast<int>(y + current_data.halo_y * disp_zoom),
789  current_data.halo + current_data.halo_mod,
790  map_location(-1, -1),
791  orientation
792  );
793  }
794 }
795 
796 std::set<map_location> unit_frame::get_overlaped_hex(const std::chrono::milliseconds& frame_time, const map_location& src, const map_location& dst,
797  const frame_parameters& animation_val, const frame_parameters& engine_val) const
798 {
800 
801  const auto [xsrc, ysrc] = disp->get_location(src);
802  const auto [xdst, ydst] = disp->get_location(dst);
803  const map_location::direction direction = src.get_relative_dir(dst);
804 
805  const frame_parameters current_data = merge_parameters(frame_time, animation_val, engine_val);
806 
807  double tmp_offset = current_data.offset;
808  const int d2 = display::get_singleton()->hex_size() / 2;
809 
810  image::locator image_loc;
811  if(direction != map_location::direction::north && direction != map_location::direction::south) {
812  image_loc = current_data.image_diagonal.clone(current_data.image_mod);
813  }
814 
815  if(image_loc.is_void() || image_loc.get_filename().empty()) { // invalid diag image, or not diagonal
816  image_loc = current_data.image.clone(current_data.image_mod);
817  }
818 
819  // We always invalidate our own hex because we need to be called at redraw time even
820  // if we don't draw anything in the hex itself
821  std::set<map_location> result;
822  if(tmp_offset == 0 && current_data.x == 0 && current_data.directional_x == 0 && image::is_in_hex(image_loc)) {
823  result.insert(src);
824 
825  bool facing_north = (
827  direction == map_location::direction::north ||
829 
830  if(!current_data.auto_vflip) { facing_north = true; }
831 
832  int my_y = current_data.y;
833  if(facing_north) {
834  my_y += current_data.directional_y;
835  } else {
836  my_y -= current_data.directional_y;
837  }
838 
839  if(my_y < 0) {
840  result.insert(src.get_direction(map_location::direction::north));
841  result.insert(src.get_direction(map_location::direction::north_east));
842  result.insert(src.get_direction(map_location::direction::north_west));
843  } else if(my_y > 0) {
844  result.insert(src.get_direction(map_location::direction::south));
845  result.insert(src.get_direction(map_location::direction::south_east));
846  result.insert(src.get_direction(map_location::direction::south_west));
847  }
848  } else {
849  int w = 0, h = 0;
850 
851  if(!image_loc.is_void() && !image_loc.get_filename().empty()) {
852  const point s = image::get_size(image_loc);
853  w = s.x;
854  h = s.y;
855  }
856 
857  if(w != 0 || h != 0) {
858  // TODO: unduplicate this code
859  const int x = static_cast<int>(tmp_offset * xdst + (1.0 - tmp_offset) * xsrc) + d2;
860  const int y = static_cast<int>(tmp_offset * ydst + (1.0 - tmp_offset) * ysrc) + d2;
861  const double disp_zoom = display::get_singleton()->get_zoom_factor();
862 
863  bool facing_west = (
866 
867  bool facing_north = (
869  direction == map_location::direction::north ||
871 
872  if(!current_data.auto_hflip) { facing_west = false; }
873  if(!current_data.auto_vflip) { facing_north = true; }
874 
875  int my_x = x + disp_zoom * (current_data.x - w / 2);
876  int my_y = y + disp_zoom * (current_data.y - h / 2);
877 
878  if(facing_west) {
879  my_x -= current_data.directional_x * disp_zoom;
880  } else {
881  my_x += current_data.directional_x * disp_zoom;
882  }
883 
884  if(facing_north) {
885  my_y += current_data.directional_y * disp_zoom;
886  } else {
887  my_y -= current_data.directional_y * disp_zoom;
888  }
889 
890  // Check if our underlying hexes are invalidated. If we need to update ourselves because we changed,
891  // invalidate our hexes and return whether or not was successful.
892  const SDL_Rect r {my_x, my_y, int(w * disp_zoom), int(h * disp_zoom)};
893  display::rect_of_hexes underlying_hex = disp->hexes_under_rect(r);
894 
895  result.insert(src);
896  result.insert(underlying_hex.begin(), underlying_hex.end());
897  } else {
898  // We have no "redraw surface" but we still need to invalidate our own hex in case we have a halo
899  // and/or sound that needs a redraw.
900  result.insert(src);
901  result.insert(dst);
902  }
903  }
904 
905  return result;
906 }
907 
908 /**
909  * This function merges the value provided by:
910  * - the frame
911  * - the engine (poison, flying unit...)
912  * - the animation as a whole
913  *
914  * There is no absolute rule for merging, so creativity is the rule. If a value is never provided by the engine, assert.
915  * This way if it becomes used, people will easily find the right place to look.
916  */
917 frame_parameters unit_frame::merge_parameters(const std::chrono::milliseconds& current_time,
918  const frame_parameters& animation_val,
919  const frame_parameters& engine_val) const
920 {
921  frame_parameters result;
922  const frame_parameters& current_val = builder_.parameters(current_time);
923 
924  result.primary_frame = engine_val.primary_frame;
925  if(!boost::logic::indeterminate(animation_val.primary_frame)) {
926  result.primary_frame = animation_val.primary_frame;
927  }
928 
929  if(!boost::logic::indeterminate(current_val.primary_frame)) {
930  result.primary_frame = current_val.primary_frame;
931  }
932 
933  // Convert the tribool to bool
934  const bool primary = static_cast<bool>(result.primary_frame) || boost::logic::indeterminate(result.primary_frame);
935 
936  /** The engine provides a default image to use for the unit when none is available */
937  result.image = current_val.image.is_void() || current_val.image.get_filename().empty()
938  ? animation_val.image
939  : current_val.image;
940 
941  if(primary && (result.image.is_void() || result.image.get_filename().empty())) {
942  result.image = engine_val.image;
943  }
944 
945  /** The engine provides a default image to use for the unit when none is available */
946  result.image_diagonal = current_val.image_diagonal.is_void() || current_val.image_diagonal.get_filename().empty()
947  ? animation_val.image_diagonal
948  : current_val.image_diagonal;
949 
950  if(primary && (result.image_diagonal.is_void() || result.image_diagonal.get_filename().empty())) {
951  result.image_diagonal = engine_val.image_diagonal;
952  }
953 
954  /**
955  * The engine provides a string for "petrified" and "team color" modifications.
956  * Note that image_mod is the complete modification and halo_mod is only the TC part.
957  */
958  result.image_mod = current_val.image_mod + animation_val.image_mod;
959  if(primary) {
960  result.image_mod += engine_val.image_mod;
961  } else {
962  result.image_mod += engine_val.halo_mod;
963  }
964 
965  assert(engine_val.halo.empty());
966  result.halo = current_val.halo.empty() ? animation_val.halo : current_val.halo;
967 
968  assert(engine_val.halo_x == 0);
969  result.halo_x = current_val.halo_x ? current_val.halo_x : animation_val.halo_x;
970 
971  /** The engine provides a y modification for terrain with height adjust and flying units */
972  result.halo_y = current_val.halo_y ? current_val.halo_y : animation_val.halo_y;
973  result.halo_y += engine_val.halo_y;
974 
975  result.halo_mod = current_val.halo_mod + animation_val.halo_mod;
976  result.halo_mod += engine_val.halo_mod;
977 
978  assert(engine_val.duration == 0ms);
979  result.duration = current_val.duration;
980 
981  assert(engine_val.sound.empty());
982  result.sound = current_val.sound.empty() ? animation_val.sound : current_val.sound;
983 
984  assert(engine_val.text.empty());
985  result.text = current_val.text.empty() ? animation_val.text : current_val.text;
986 
987  assert(!engine_val.text_color);
988  result.text_color = current_val.text_color ? current_val.text_color : animation_val.text_color;
989 
990  /** The engine provides a blend color for poisoned units */
991  result.blend_with = current_val.blend_with ? current_val.blend_with : animation_val.blend_with;
992  if(primary && engine_val.blend_with) {
993  result.blend_with = engine_val.blend_with->blend_lighten(result.blend_with ? *result.blend_with : color_t(0,0,0));
994  }
995 
996  /** The engine provides a blend color for poisoned units */
997  result.blend_ratio = current_val.blend_ratio != 0 ? current_val.blend_ratio:animation_val.blend_ratio;
998  if(primary && engine_val.blend_ratio != 0) {
999  result.blend_ratio = std::min(result.blend_ratio + engine_val.blend_ratio, 1.0);
1000  }
1001 
1002  /** The engine provides a highlight ratio for selected units and visible "invisible" units */
1003  result.highlight_ratio = (current_val.highlight_ratio < 0.999 || current_val.highlight_ratio > 1.001) ?
1004  current_val.highlight_ratio : animation_val.highlight_ratio;
1005  if(primary && (engine_val.highlight_ratio < 0.999 || engine_val.highlight_ratio > 1.001)) {
1006  result.highlight_ratio = result.highlight_ratio * engine_val.highlight_ratio; // selected unit
1007  }
1008 
1009  assert(engine_val.offset == 0);
1010  result.offset = (current_val.offset != -1000) ? current_val.offset : animation_val.offset;
1011  if(result.offset == -1000) {
1012  result.offset = 0.0;
1013  }
1014 
1015  /** The engine provides a submerge for units in water */
1016  result.submerge = current_val.submerge != 0 ? current_val.submerge : animation_val.submerge;
1017  if(primary && engine_val.submerge != 0 && result.submerge == 0) {
1018  result.submerge = engine_val.submerge;
1019  }
1020 
1021  assert(engine_val.x == 0);
1022  result.x = current_val.x ? current_val.x : animation_val.x;
1023 
1024  /** The engine provides a y modification for terrain with height adjust and flying units */
1025  result.y = current_val.y?current_val.y:animation_val.y;
1026  result.y += engine_val.y;
1027 
1028  assert(engine_val.directional_x == 0);
1029  result.directional_x = current_val.directional_x ? current_val.directional_x : animation_val.directional_x;
1030 
1031  assert(engine_val.directional_y == 0);
1032  result.directional_y = current_val.directional_y ? current_val.directional_y : animation_val.directional_y;
1033 
1036  ? current_val.drawing_layer
1037  : animation_val.drawing_layer;
1038 
1039  /** The engine provides us with a default value to compare to. Update if different */
1040  result.auto_hflip = engine_val.auto_hflip;
1041 
1042  if(!boost::logic::indeterminate(animation_val.auto_hflip)) {
1043  result.auto_hflip = animation_val.auto_hflip;
1044  }
1045 
1046  if(!boost::logic::indeterminate(current_val.auto_hflip)) {
1047  result.auto_hflip = current_val.auto_hflip;
1048  }
1049 
1050  if(boost::logic::indeterminate(result.auto_hflip)) {
1051  result.auto_hflip = true;
1052  }
1053 
1054  result.auto_vflip = engine_val.auto_vflip;
1055 
1056  if(!boost::logic::indeterminate(animation_val.auto_vflip)) {
1057  result.auto_vflip = animation_val.auto_vflip;
1058  }
1059 
1060  if(!boost::logic::indeterminate(current_val.auto_vflip)) {
1061  result.auto_vflip = current_val.auto_vflip;
1062  }
1063 
1064  if(boost::logic::indeterminate(result.auto_vflip)) {
1065  result.auto_vflip = !primary;
1066  }
1067 
1068  return result;
1069 }
map_location loc
Definition: move.cpp:172
Variant for storing WML attributes.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:172
bool has_attribute(config_key_type key) const
Definition: config.cpp:157
const attribute_value * get(config_key_type key) const
Returns a pointer to the attribute with the given key or nullptr if it does not exist.
Definition: config.cpp:685
Sort-of-Singleton that many classes, both GUI and non-GUI, use to access the game data.
Definition: display.hpp:97
point get_location(const map_location &loc) const
Functions to get the on-screen positions of hexes.
Definition: display.cpp:679
static int hex_size()
Function which returns the size of a hex in pixels (from top tip to bottom tip or left edge to right ...
Definition: display.hpp:267
static double get_zoom_factor()
Returns the current zoom factor.
Definition: display.hpp:270
static submerge_data get_submerge_data(const rect &dest, double submerge, const point &size, uint8_t alpha, bool hreverse, bool vreverse)
Definition: display.cpp:2153
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:1258
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:626
rect map_area() const
Returns the area used for the map.
Definition: display.cpp:495
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:111
static rect scaled_to_zoom(const SDL_Rect &r)
Scale the width and height of a rect by the current zoom factor.
Definition: display.hpp:276
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
frame_builder()
Definition: frame.cpp:28
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
std::string text_
Definition: frame.hpp:185
progressive_int directional_x_
Definition: frame.hpp:196
progressive_int halo_y_
Definition: frame.hpp:181
boost::tribool auto_hflip_
Definition: frame.hpp:200
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::string sound_
Definition: frame.hpp:184
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
const std::chrono::milliseconds & duration() const
Definition: frame.hpp:164
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:420
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
const std::string & get_modifications() const
Definition: picture.hpp:87
locator clone(const std::string &mods) const
Returns a copy of this locator with the given IPF.
Definition: picture.cpp:218
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)
Blend mode.
Definition: texture.cpp:191
void set_src(const rect &r)
Set the source region of the texture used for drawing operations.
Definition: texture.cpp:129
void set_alpha_mod(uint8_t alpha)
Alpha modifier.
Definition: texture.cpp:151
void set_color_mod(uint8_t r, uint8_t g, uint8_t b)
Colour modifier.
Definition: texture.cpp:174
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:630
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:796
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:917
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:280
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.
static lg::log_domain log_engine("engine")
#define ERR_NG
Definition: frame.cpp:26
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
int w
Standard logging facilities (interface).
void flipped(const texture &tex, const SDL_Rect &dst, bool flip_h=true, bool flip_v=false)
Draws a texture, or part of a texture, at the given location, also mirroring/flipping the texture hor...
Definition: draw.cpp:340
void smooth_shaded(const texture &tex, const SDL_Rect &dst, const SDL_Color &cTL, const SDL_Color &cTR, const SDL_Color &cBL, const SDL_Color &cBR, const SDL_FPoint &uvTL, const SDL_FPoint &uvTR, const SDL_FPoint &uvBL, const SDL_FPoint &uvBR)
Draw a texture with smoothly varying colour and alpha modification, specified at the four corners of ...
Definition: draw.cpp:431
Definition: halo.cpp:39
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:31
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:920
point get_size(const locator &i_locator, bool skip_cache)
Returns the width and height of an image.
Definition: picture.cpp:777
bool is_in_hex(const locator &i_locator)
Checks if an image fits into a single hex.
Definition: picture.cpp:786
Audio output for sound and music.
Definition: sound.cpp:42
void play_sound(const std::string &files, channel_group group, unsigned int repeats)
Definition: sound.cpp:1047
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'.
std::string_view data
Definition: picture.cpp:178
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:59
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:328
iterator end() const
Definition: display.cpp:621
iterator begin() const
Definition: display.cpp:617
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
int directional_x
Definition: frame.hpp:70
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
int drawing_layer
Definition: frame.hpp:77
image::locator image_diagonal
Definition: frame.hpp:48
boost::tribool primary_frame
Definition: frame.hpp:75
int directional_y
Definition: frame.hpp:71
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:45
direction
Valid directions which can be moved in our hexagonal world.
Definition: location.hpp:47
Holds a 2D point.
Definition: point.hpp:25
An abstract description of a rectangle with integer coordinates.
Definition: rect.hpp:47
bool overlaps(const SDL_Rect &r) const
Whether the given rectangle and this rectangle overlap.
Definition: rect.cpp:73
static map_location::direction s
#define e
#define h