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