The Battle for Wesnoth  1.15.0-dev
frame.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2018 by Jeremy Rosen <jeremy.rosen@enst-bretagne.fr>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #include "units/frame.hpp"
16 
17 #include "color.hpp"
18 #include "game_display.hpp"
19 #include "log.hpp"
20 #include "map/hex_rect.hpp"
21 #include "sdl/render_utils.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  , layer(drawing_queue::LAYER_UNIT_DEFAULT - drawing_queue::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  , layer_(std::to_string(drawing_queue::LAYER_UNIT_DEFAULT - drawing_queue::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  , layer_(cfg[frame_string + "layer"])
76 {
77  if(!cfg.has_attribute(frame_string + "auto_vflip")) {
78  auto_vflip_ = boost::logic::indeterminate;
79  } else {
80  auto_vflip_ = cfg[frame_string + "auto_vflip"].to_bool();
81  }
82 
83  if(!cfg.has_attribute(frame_string + "auto_hflip")) {
84  auto_hflip_ = boost::logic::indeterminate;
85  } else {
86  auto_hflip_ = cfg[frame_string + "auto_hflip"].to_bool();
87  }
88 
89  if(!cfg.has_attribute(frame_string + "primary")) {
90  primary_frame_ = boost::logic::indeterminate;
91  } else {
92  primary_frame_ = cfg[frame_string + "primary"].to_bool();
93  }
94 
95  try {
96  text_color_ = color_t::from_rgb_string(cfg[frame_string + "text_color"]);
97  } catch(const std::invalid_argument& e) {
98  // Might be thrown either due to an incorrect number of elements or std::stoul failure.
99  ERR_NG << "Invalid RBG text color in unit animation: " << cfg[frame_string + "text_color"].str()
100  << "\n" << e.what() << "\n;";
101  }
102 
103  if(const config::attribute_value* v = cfg.get(frame_string + "duration")) {
104  duration(*v);
105  } else if(!cfg.get(frame_string + "end")) {
106  int halo_duration = (progressive_string(halo_, 1)).duration();
107  int image_duration = (progressive_image(image_, 1)).duration();
108  int image_diagonal_duration = (progressive_image(image_diagonal_, 1)).duration();
109 
110  duration(std::max(std::max(image_duration, image_diagonal_duration), halo_duration));
111  } else {
112  duration(cfg[frame_string + "end"].to_int() - cfg[frame_string + "begin"].to_int());
113  }
114 
115  duration_ = std::max(duration_, 1);
116 
117  try {
118  blend_with_ = color_t::from_rgb_string(cfg[frame_string + "blend_color"]);
119  } catch(const std::invalid_argument& e) {
120  // Might be thrown either due to an incorrect number of elements or std::stoul failure.
121  ERR_NG << "Invalid RBG blend color in unit animation: " << cfg[frame_string + "blend_color"].str()
122  << "\n" << e.what() << "\n;";
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 
176 {
178  return *this;
179 }
180 
182 {
183  offset_ = offset;
184  return *this;
185 }
186 
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 
206 {
208  return *this;
209 }
210 
212 {
214  return *this;
215 }
216 
218 {
220  return *this;
221 }
222 
224 {
226  return *this;
227 }
228 
230 {
232  return *this;
233 }
234 
236 {
237  layer_=layer;
238  return *this;
239 }
240 
242  : duration_(duration ? duration : builder.duration_)
243  , image_(builder.image_,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_)
256  , offset_(builder.offset_,duration_)
257  , submerge_(builder.submerge_,duration_)
258  , x_(builder.x_,duration_)
259  , y_(builder.y_,duration_)
262  , auto_vflip_(builder.auto_vflip_)
263  , auto_hflip_(builder.auto_hflip_)
264  , primary_frame_(builder.primary_frame_)
265  , layer_(builder.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()) {
331  highlight_ratio_ = progressive_double(highlight,duration);
332  } else if(duration != duration_){
334  }
335 
336  if(!offset.empty()) {
337  offset_ = progressive_double(offset,duration);
338  } else if(duration != duration_){
340  }
341 
342  if(!blend_ratio.empty()) {
343  blend_ratio_ = progressive_double(blend_ratio,duration);
344  blend_with_ = blend_color;
345  } else if(duration != duration_){
347  }
348 
349  if(!layer.empty()) {
350  layer_ = progressive_int(layer,duration);
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(auto_vflip_));
460  }
461 
462  if(!boost::indeterminate(auto_hflip_)) {
463  v.emplace_back("auto_hflip=" + utils::bool_string(auto_hflip_));
464  }
465 
466  if(!boost::indeterminate(primary_frame_)) {
467  v.emplace_back("primary_frame=" + utils::bool_string(primary_frame_));
468  }
469 
470  if(!layer_.get_original().empty()) {
471  v.emplace_back("layer=" + layer_.get_original());
472  }
473 
474  return v;
475 }
476 
477 void unit_frame::redraw(const int frame_time, bool on_start_time, bool in_scope_of_frame,
478  const map_location& src, const map_location& dst,
479  halo::handle& halo_id, halo::manager& halo_man,
480  const frame_parameters& animation_val, const frame_parameters& engine_val) const
481 {
483 
484  const int xsrc = game_disp->get_location_x(src);
485  const int ysrc = game_disp->get_location_y(src);
486  const int xdst = game_disp->get_location_x(dst);
487  const int ydst = game_disp->get_location_y(dst);
488  const map_location::DIRECTION direction = src.get_relative_dir(dst);
489 
490  const frame_parameters current_data = merge_parameters(frame_time,animation_val,engine_val);
491  double tmp_offset = current_data.offset;
492 
493  // Debug code to see the number of frames and their position
494  //if(tmp_offset) {
495  // std::cout << static_cast<int>(tmp_offset * 100) << "," << "\n";
496  //}
497 
498  if(on_start_time) {
499  // Stuff that should be done only once per frame
500  if(!current_data.sound.empty() ) {
501  sound::play_sound(current_data.sound);
502  }
503 
504  if(!current_data.text.empty() && current_data.text_color) {
505  game_disp->float_label(src, current_data.text, *current_data.text_color);
506  }
507  }
508 
509  image::locator image_loc;
510  if(direction != map_location::NORTH && direction != map_location::SOUTH) {
511  image_loc = image::locator(current_data.image_diagonal, current_data.image_mod);
512  }
513 
514  if(image_loc.is_void() || image_loc.get_filename().empty()) { // invalid diag image, or not diagonal
515  image_loc = image::locator(current_data.image, current_data.image_mod);
516  }
517 
518  texture image;
519  if(!image_loc.is_void() && !image_loc.get_filename().empty()) { // invalid diag image, or not diagonal
520  image = image::get_texture(image_loc /*, image::SCALED_TO_ZOOM*/);
521  }
522 
523  const int d2 = display::get_singleton()->hex_size() / 2;
524 
525  const int x = static_cast<int>(tmp_offset * xdst + (1.0 - tmp_offset) * xsrc) + d2;
526  const int y = static_cast<int>(tmp_offset * ydst + (1.0 - tmp_offset) * ysrc) + d2;
527 
528  if(image != nullptr) {
529  bool facing_west = (
530  direction == map_location::NORTH_WEST ||
531  direction == map_location::SOUTH_WEST);
532 
533  bool facing_north = (
534  direction == map_location::NORTH_WEST ||
535  direction == map_location::NORTH ||
536  direction == map_location::NORTH_EAST);
537 
538  if(!current_data.auto_hflip) { facing_west = false; }
539  if(!current_data.auto_vflip) { facing_north = true; }
540 
541  const texture::info info = image.get_info();
542 
543  int my_x = x + current_data.x - ((info.w / 2) * game_disp->get_zoom_factor());
544  int my_y = y + current_data.y - ((info.h / 2) * game_disp->get_zoom_factor());
545 
546  if(facing_west) {
547  my_x -= current_data.directional_x;
548  } else {
549  my_x += current_data.directional_x;
550  }
551 
552  if(facing_north) {
553  my_y += current_data.directional_y;
554  } else {
555  my_y -= current_data.directional_y;
556  }
557 
558  //
559  // Begin unit drawing routine.
560  //
561 
562  fixed_t alpha = ftofxp(current_data.highlight_ratio);
563 
564  // Explicitly set sprite to opaque if not applying an alpha effect. This ensures that
565  // multiple copies of the same sprite - ie, diffent units of the same type - don't all
566  // get drawn with the alpha effect. This happened because they all use the same texture
567  // and setting the alpha mod on one affects them all.
568  set_texture_alpha(image, alpha < ftofxp(1.0) ? alpha : ALPHA_OPAQUE);
569 
570 #if 0
571  surface surf(image);
572 
573  color_t blend_to = current_data.blend_with ? *current_data.blend_with : color_t();
574 
575  if(current_data.blend_ratio != 0) {
576  surf = blend_surface(surf, current_data.blend_ratio, blend_to);
577  }
578 
579  if(alpha > ftofxp(1.0)) {
580  surf = brighten_image(surf, alpha);
581  //} else if(alpha != 1.0 && blendto != 0) {
582  // surf.assign(blend_surface(surf,1.0-alpha,blendto));
583  } else if(alpha != ftofxp(1.0)) {
584  surface temp = make_neutral_surface(surf);
585  adjust_surface_alpha(temp, alpha);
586  surf = temp;
587  }
588 
589  if(surf == nullptr) {
590  ERR_DP << "surface lost..." << std::endl;
591  return;
592  }
593 
594  if(current_data.submerge > 0.0) {
595  // Divide the surface into 2 parts
596  const int submerge_height = std::max<int>(0, surf->h*(1.0-current_data.submerge));
597  const int depth = surf->h - submerge_height;
598 
599  SDL_Rect srcrect {0, 0, surf->w, submerge_height};
600 
601  //drawing_queue_add(layer, loc, x, y, surf, srcrect);
602 
603  if(submerge_height != surf->h) {
604  //the lower part will be transparent
605  float alpha_base = 0.3f; // 30% alpha at surface of water
606  float alpha_delta = 0.015f; // lose 1.5% per pixel depth
607  alpha_delta *= zoom_ / DefaultZoom; // adjust with zoom
608  surf = submerge_alpha(surf, depth, alpha_base, alpha_delta);
609 
610  srcrect.y = submerge_height;
611  srcrect.h = surf->h-submerge_height;
612  y += submerge_height;
613 
614  //drawing_queue_add(layer, loc, x, y, surf, srcrect);
615  }
616  }
617 #endif
618 
619  game_disp->render_scaled_to_zoom(image, my_x, my_y, facing_west, !facing_north);
620  }
621 
622  halo_id.reset();
623 
624  if(!in_scope_of_frame) { //check after frame as first/last frame image used in defense/attack anims
625  return;
626  }
627 
628  // No halos, exit
629  if(current_data.halo.empty()) {
630  return;
631  }
632 
633  halo::ORIENTATION orientation;
634  switch(direction)
635  {
636  case map_location::NORTH:
638  orientation = halo::NORMAL;
639  break;
641  case map_location::SOUTH:
642  if(!current_data.auto_vflip) {
643  orientation = halo::NORMAL;
644  } else {
645  orientation = halo::VREVERSE;
646  }
647  break;
649  if(!current_data.auto_vflip) {
650  orientation = halo::HREVERSE;
651  } else {
652  orientation = halo::HVREVERSE;
653  }
654  break;
656  orientation = halo::HREVERSE;
657  break;
659  default:
660  orientation = halo::NORMAL;
661  break;
662  }
663 
664  if(direction != map_location::SOUTH_WEST && direction != map_location::NORTH_WEST) {
665  halo_id = halo_man.add(
666  static_cast<int>(x + current_data.halo_x * display::get_singleton()->get_zoom_factor()),
667  static_cast<int>(y + current_data.halo_y * display::get_singleton()->get_zoom_factor()),
668  current_data.halo + current_data.halo_mod,
669  map_location(-1, -1),
670  orientation
671  );
672  } else {
673  halo_id = halo_man.add(
674  static_cast<int>(x - current_data.halo_x * display::get_singleton()->get_zoom_factor()),
675  static_cast<int>(y + current_data.halo_y * display::get_singleton()->get_zoom_factor()),
676  current_data.halo + current_data.halo_mod,
677  map_location(-1, -1),
678  orientation
679  );
680  }
681 }
682 
683 std::set<map_location> unit_frame::get_overlaped_hex(const int frame_time, const map_location& src, const map_location& dst,
684  const frame_parameters& animation_val, const frame_parameters& engine_val) const
685 {
687 
688  const int xsrc = disp->get_location_x(src);
689  const int ysrc = disp->get_location_y(src);
690  const int xdst = disp->get_location_x(dst);
691  const int ydst = disp->get_location_y(dst);
692  const map_location::DIRECTION direction = src.get_relative_dir(dst);
693 
694  const frame_parameters current_data = merge_parameters(frame_time, animation_val, engine_val);
695 
696  double tmp_offset = current_data.offset;
697  const int d2 = display::get_singleton()->hex_size() / 2;
698 
699  image::locator image_loc;
700  if(direction != map_location::NORTH && direction != map_location::SOUTH) {
701  image_loc = image::locator(current_data.image_diagonal, current_data.image_mod);
702  }
703 
704  if(image_loc.is_void() || image_loc.get_filename().empty()) { // invalid diag image, or not diagonal
705  image_loc = image::locator(current_data.image, current_data.image_mod);
706  }
707 
708  // We always invalidate our own hex because we need to be called at redraw time even
709  // if we don't draw anything in the hex itself
710  std::set<map_location> result;
711  if(tmp_offset == 0 && current_data.x == 0 && current_data.directional_x == 0 && image::is_in_hex(image_loc)) {
712  result.insert(src);
713 
714  bool facing_north = (
715  direction == map_location::NORTH_WEST ||
716  direction == map_location::NORTH ||
717  direction == map_location::NORTH_EAST);
718 
719  if(!current_data.auto_vflip) { facing_north = true; }
720 
721  int my_y = current_data.y;
722  if(facing_north) {
723  my_y += current_data.directional_y;
724  } else {
725  my_y -= current_data.directional_y;
726  }
727 
728  if(my_y < 0) {
729  result.insert(src.get_direction(map_location::NORTH));
730  result.insert(src.get_direction(map_location::NORTH_EAST));
731  result.insert(src.get_direction(map_location::NORTH_WEST));
732  } else if(my_y > 0) {
733  result.insert(src.get_direction(map_location::SOUTH));
734  result.insert(src.get_direction(map_location::SOUTH_EAST));
735  result.insert(src.get_direction(map_location::SOUTH_WEST));
736  }
737  } else {
738  int w = 0, h = 0;
739 
740  {
741  surface image;
742  if(!image_loc.is_void() && !image_loc.get_filename().empty()) { // invalid diag image, or not diagonal
743  image = image::get_image(image_loc, image::SCALED_TO_ZOOM);
744  }
745 
746  if(image != nullptr) {
747  w = image->w;
748  h = image->h;
749  }
750  }
751 
752  if(w != 0 || h != 0) {
753  const int x = static_cast<int>(tmp_offset * xdst + (1.0 - tmp_offset) * xsrc);
754  const int y = static_cast<int>(tmp_offset * ydst + (1.0 - tmp_offset) * ysrc);
755 
756  bool facing_west = (
757  direction == map_location::NORTH_WEST ||
758  direction == map_location::SOUTH_WEST);
759 
760  bool facing_north = (
761  direction == map_location::NORTH_WEST ||
762  direction == map_location::NORTH ||
763  direction == map_location::NORTH_EAST);
764 
765  if(!current_data.auto_vflip) { facing_north = true; }
766  if(!current_data.auto_hflip) { facing_west = false; }
767 
768  int my_x = x + current_data.x + d2 - w / 2;
769  int my_y = y + current_data.y + d2 - h / 2;
770 
771  if(facing_west) {
772  my_x += current_data.directional_x;
773  } else {
774  my_x -= current_data.directional_x;
775  }
776 
777  if(facing_north) {
778  my_y += current_data.directional_y;
779  } else {
780  my_y -= current_data.directional_y;
781  }
782 
783  // Check if our underlying hexes are invalidated. If we need to update ourselves because we changed,
784  // invalidate our hexes and return whether or not was successful.
785  const SDL_Rect r {my_x, my_y, w, h};
786  rect_of_hexes underlying_hex = disp->hexes_under_rect(r);
787 
788  result.insert(src);
789  result.insert(underlying_hex.begin(), underlying_hex.end());
790  } else {
791  // We have no "redraw surface" but we still need to invalidate our own hex in case we have a halo
792  // and/or sound that needs a redraw.
793  result.insert(src);
794  result.insert(dst);
795  }
796  }
797 
798  return result;
799 }
800 
801 /**
802  * This function merges the value provided by:
803  * - the frame
804  * - the engine (poison, flying unit...)
805  * - the animation as a whole
806  *
807  * There is no absolute rule for merging, so creativity is the rule. If a value is never provided by the engine, assert.
808  * This way if it becomes used, people will easily find the right place to look.
809  */
810 const frame_parameters unit_frame::merge_parameters(int current_time, const frame_parameters& animation_val,
811  const frame_parameters& engine_val) const
812 {
813  frame_parameters result;
814  const frame_parameters& current_val = builder_.parameters(current_time);
815 
816  result.primary_frame = engine_val.primary_frame;
817  if(!boost::logic::indeterminate(animation_val.primary_frame)) {
818  result.primary_frame = animation_val.primary_frame;
819  }
820 
821  if(!boost::logic::indeterminate(current_val.primary_frame)) {
822  result.primary_frame = current_val.primary_frame;
823  }
824 
825  // Convert the tribool to bool
826  const bool primary = result.primary_frame == true || boost::logic::indeterminate(result.primary_frame);
827 
828  /** The engine provides a default image to use for the unit when none is available */
829  result.image = current_val.image.is_void() || current_val.image.get_filename().empty()
830  ? animation_val.image
831  : current_val.image;
832 
833  if(primary && (result.image.is_void() || result.image.get_filename().empty())) {
834  result.image = engine_val.image;
835  }
836 
837  /** The engine provides a default image to use for the unit when none is available */
838  result.image_diagonal = current_val.image_diagonal.is_void() || current_val.image_diagonal.get_filename().empty()
839  ? animation_val.image_diagonal
840  : current_val.image_diagonal;
841 
842  if(primary && (result.image_diagonal.is_void() || result.image_diagonal.get_filename().empty())) {
843  result.image_diagonal = engine_val.image_diagonal;
844  }
845 
846  /**
847  * The engine provides a string for "petrified" and "team color" modifications.
848  * Note that image_mod is the complete modification and halo_mod is only the TC part.
849  */
850  result.image_mod = current_val.image_mod + animation_val.image_mod;
851  if(primary) {
852  result.image_mod += engine_val.image_mod;
853  } else {
854  result.image_mod += engine_val.halo_mod;
855  }
856 
857  assert(engine_val.halo.empty());
858  result.halo = current_val.halo.empty() ? animation_val.halo : current_val.halo;
859 
860  assert(engine_val.halo_x == 0);
861  result.halo_x = current_val.halo_x ? current_val.halo_x : animation_val.halo_x;
862 
863  /** The engine provides a y modification for terrain with height adjust and flying units */
864  result.halo_y = current_val.halo_y ? current_val.halo_y : animation_val.halo_y;
865  result.halo_y += engine_val.halo_y;
866 
867  result.halo_mod = current_val.halo_mod + animation_val.halo_mod;
868  result.halo_mod += engine_val.halo_mod;
869 
870  assert(engine_val.duration == 0);
871  result.duration = current_val.duration;
872 
873  assert(engine_val.sound.empty());
874  result.sound = current_val.sound.empty() ? animation_val.sound : current_val.sound;
875 
876  assert(engine_val.text.empty());
877  result.text = current_val.text.empty() ? animation_val.text : current_val.text;
878 
879  assert(!engine_val.text_color);
880  result.text_color = current_val.text_color ? current_val.text_color : animation_val.text_color;
881 
882  /** The engine provides a blend color for poisoned units */
883  result.blend_with = current_val.blend_with ? current_val.blend_with : animation_val.blend_with;
884  if(primary && engine_val.blend_with) {
885  result.blend_with = engine_val.blend_with->blend_lighten(result.blend_with ? *result.blend_with : color_t(0,0,0));
886  }
887 
888  /** The engine provides a blend color for poisoned units */
889  result.blend_ratio = current_val.blend_ratio?current_val.blend_ratio:animation_val.blend_ratio;
890  if(primary && engine_val.blend_ratio) {
891  result.blend_ratio = std::min(result.blend_ratio + engine_val.blend_ratio, 1.0);
892  }
893 
894  /** The engine provides a highlight ratio for selected units and visible "invisible" units */
895  result.highlight_ratio = (current_val.highlight_ratio < 0.999 || current_val.highlight_ratio > 1.001) ?
896  current_val.highlight_ratio : animation_val.highlight_ratio;
897  if(primary && (engine_val.highlight_ratio < 0.999 || engine_val.highlight_ratio > 1.001)) {
898  result.highlight_ratio = result.highlight_ratio * engine_val.highlight_ratio; // selected unit
899  }
900 
901  assert(engine_val.offset == 0);
902  result.offset = (current_val.offset != -1000) ? current_val.offset : animation_val.offset;
903  if(result.offset == -1000) {
904  result.offset = 0.0;
905  }
906 
907  /** The engine provides a submerge for units in water */
908  result.submerge = current_val.submerge ? current_val.submerge : animation_val.submerge;
909  if(primary && engine_val.submerge && !result.submerge) {
910  result.submerge = engine_val.submerge;
911  }
912 
913  assert(engine_val.x == 0);
914  result.x = current_val.x ? current_val.x : animation_val.x;
915 
916  /** The engine provides a y modification for terrain with height adjust and flying units */
917  result.y = current_val.y?current_val.y:animation_val.y;
918  result.y += engine_val.y;
919 
920  assert(engine_val.directional_x == 0);
921  result.directional_x = current_val.directional_x ? current_val.directional_x : animation_val.directional_x;
922 
923  assert(engine_val.directional_y == 0);
924  result.directional_y = current_val.directional_y ? current_val.directional_y : animation_val.directional_y;
925 
928  ? current_val.layer
929  : animation_val.layer;
930 
931  /** The engine provides us with a default value to compare to. Update if different */
932  result.auto_hflip = engine_val.auto_hflip;
933 
934  if(!boost::logic::indeterminate(animation_val.auto_hflip)) {
935  result.auto_hflip = animation_val.auto_hflip;
936  }
937 
938  if(!boost::logic::indeterminate(current_val.auto_hflip)) {
939  result.auto_hflip = current_val.auto_hflip;
940  }
941 
942  if(boost::logic::indeterminate(result.auto_hflip)) {
943  result.auto_hflip = true;
944  }
945 
946  result.auto_vflip = engine_val.auto_vflip;
947 
948  if(!boost::logic::indeterminate(animation_val.auto_vflip)) {
949  result.auto_vflip = animation_val.auto_vflip;
950  }
951 
952  if(!boost::logic::indeterminate(current_val.auto_vflip)) {
953  result.auto_vflip = current_val.auto_vflip;
954  }
955 
956  if(boost::logic::indeterminate(result.auto_vflip)) {
957  result.auto_vflip = !primary;
958  }
959 
960  return result;
961 }
const frame_parameters parameters(int current_time) const
Getters for the different parameters.
Definition: frame.cpp:292
Reserve layers to be selected for WML.
virtual bool does_not_change() const
surface get_image(const image::locator &i_locator, TYPE type)
function to get the surface corresponding to an image.
Definition: image.cpp:989
virtual const T get_current_element(int current_time, T default_val=T()) const override
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:683
std::string image_
Definition: frame.hpp:108
frame_builder & y(const std::string &y)
Definition: frame.cpp:199
std::string sound
Definition: frame.hpp:50
boost::tribool auto_hflip_
Definition: frame.hpp:193
All parameters from a frame at a given instant.
Definition: frame.hpp:34
boost::tribool auto_hflip
Definition: frame.hpp:67
image::locator image
Definition: frame.hpp:40
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:99
progressive_int halo_x_
Definition: frame.hpp:173
progressive_single< image::locator > progressive_image
progressive_double blend_ratio_
Definition: frame.hpp:183
#define DefaultZoom
Definition: display.cpp:63
frame_parsed_parameters(const frame_builder &builder=frame_builder(), int override_duration=0)
Definition: frame.cpp:241
frame_builder & blend(const std::string &blend_ratio, const color_t blend_color)
Definition: frame.cpp:168
DIRECTION get_relative_dir(const map_location &loc, map_location::RELATIVE_DIR_MODE mode) const
Definition: location.cpp:225
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:321
std::string image_mod_
Definition: frame.hpp:170
std::string blend_ratio_
Definition: frame.hpp:121
std::string sound_
Definition: frame.hpp:177
Variant for storing WML attributes.
std::string directional_y_
Definition: frame.hpp:128
std::string text_
Definition: frame.hpp:116
bool has_attribute(config_key_type key) const
Definition: config.cpp:217
logger & info()
Definition: log.cpp:90
boost::optional< color_t > text_color
Definition: frame.hpp:53
Frame for unit&#39;s animation sequence.
frame_builder()
Definition: frame.cpp:45
iterator begin() const
Definition: hex_rect.hpp:100
int directional_x
Definition: frame.hpp:63
frame_builder & image_diagonal(const std::string &image_diagonal, const std::string &image_mod="")
Definition: frame.cpp:133
surface surf
Image.
progressive_int layer_
Definition: frame.hpp:196
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:695
std::string get_original() const
STL namespace.
#define h
std::string text
Definition: frame.hpp:51
Audio output for sound and music.
Definition: sound.cpp:40
std::string halo_mod_
Definition: frame.hpp:114
int directional_y
Definition: frame.hpp:64
frame_builder & directional_x(const std::string &directional_x)
Definition: frame.cpp:205
std::string halo_y_
Definition: frame.hpp:113
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
frame_builder & image(const std::string &image, const std::string &image_mod="")
Definition: frame.cpp:126
image::locator image_diagonal
Definition: frame.hpp:41
Wrapper class to encapsulate creation and management of an SDL_Texture.
Definition: texture.hpp:26
boost::tribool primary_frame_
Definition: frame.hpp:132
std::string halo
Definition: frame.hpp:44
std::string halo_
Definition: frame.hpp:111
void play_sound(const std::string &files, channel_group group, unsigned int repeats)
Definition: sound.cpp:989
map_location get_direction(DIRECTION dir, unsigned int n=1u) const
Definition: location.cpp:402
frame_builder & layer(const std::string &layer)
Definition: frame.cpp:235
frame_builder & submerge(const std::string &submerge)
Definition: frame.cpp:187
bool does_not_change() const override
virtual const T get_current_element(int current_time, T default_val=T()) const override
frame_builder & auto_hflip(const bool auto_hflip)
Definition: frame.cpp:223
std::string half_signed_value(int val)
Sign with Unicode "−" if negative.
boost::optional< color_t > text_color_
Definition: frame.hpp:118
frame_builder & sound(const std::string &sound)
Definition: frame.cpp:140
std::string halo_mod
Definition: frame.hpp:49
surface submerge_alpha(const surface &surf, int depth, float alpha_base, float alpha_delta)
Progressively reduce alpha of bottom part of the surface.
Definition: utils.cpp:1337
progressive_image image_diagonal_
Definition: frame.hpp:168
std::string highlight_ratio_
Definition: frame.hpp:122
progressive_int x_
Definition: frame.hpp:187
boost::tribool primary_frame_
Definition: frame.hpp:194
std::string text_
Definition: frame.hpp:178
void set_texture_alpha(texture &t, uint8_t amount)
frame_builder & auto_vflip(const bool auto_vflip)
Definition: frame.cpp:217
Rectangular area of hexes.
Definition: hex_rect.hpp:33
progressive_image image_
Definition: frame.hpp:167
progressive_double highlight_ratio_
Definition: frame.hpp:184
std::string y_
Definition: frame.hpp:126
progressive_int halo_y_
Definition: frame.hpp:174
progressive_pair< int > progressive_int
int32_t fixed_t
Definition: math.hpp:292
std::string directional_x_
Definition: frame.hpp:127
std::vector< std::string > debug_strings() const
Contents of frame in strings.
Definition: frame.cpp:374
surface blend_surface(const surface &surf, const double amount, const color_t color)
Blends a surface with a color.
Definition: utils.cpp:1805
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 &#39;image centered on (x,y).
Definition: halo.cpp:299
#define ftofxp(x)
IN: float or int - OUT: fixed_t.
Definition: math.hpp:297
progressive_int directional_x_
Definition: frame.hpp:189
Encapsulates the map of the game.
Definition: location.hpp:42
void float_label(const map_location &loc, const std::string &text, const color_t &color)
Function to float a label above a tile.
std::string layer_
Definition: frame.hpp:134
progressive_single< std::string > progressive_string
boost::optional< color_t > blend_with
Definition: frame.hpp:54
boost::optional< color_t > text_color_
Definition: frame.hpp:180
surface brighten_image(const surface &surf, fixed_t amount)
Definition: utils.cpp:1133
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:477
surface make_neutral_surface(const surface &surf)
Definition: utils.cpp:73
static lg::log_domain log_engine("engine")
double highlight_ratio
Definition: frame.hpp:57
boost::optional< color_t > blend_with_
Definition: frame.hpp:181
int duration() const
Definition: frame.hpp:157
void render_scaled_to_zoom(const texture &tex, const int x_pos, const int y_pos, T &&... extra_args) const
Renders a texture directly to the screen (or current rendering target) scaled to the current zoom fac...
Definition: display.hpp:766
std::string image_mod
Definition: frame.hpp:43
int get_location_y(const map_location &loc) const
Definition: display.cpp:626
int duration_
Definition: frame.hpp:106
std::string bool_string(const bool value)
Converts a bool value to &#39;true&#39; or &#39;false&#39;.
frame_builder & offset(const std::string &offset)
Definition: frame.cpp:181
bool need_update() const
Definition: frame.cpp:287
frame_builder & x(const std::string &x)
Definition: frame.cpp:193
std::string offset_
Definition: frame.hpp:123
DIRECTION
Valid directions which can be moved in our hexagonal world.
Definition: location.hpp:44
boost::tribool auto_vflip
Definition: frame.hpp:66
default layer for drawing units
int w
double get_zoom_factor() const
Returns the current zoom factor.
Definition: display.hpp:327
const std::string & get_filename() const
Definition: image.hpp:86
frame_builder & primary_frame(const bool primary_frame)
Definition: frame.cpp:229
bool is_in_hex(const locator &i_locator)
function to check if an image fit into an hex return false if the image has not the standard size...
Definition: image.cpp:1120
const info get_info() const
Queries metadata about the texture, such as its dimensions.
Definition: texture.hpp:55
frame_builder & directional_y(const std::string &directional_y)
Definition: frame.cpp:211
double blend_ratio
Definition: frame.hpp:56
progressive_double submerge_
Definition: frame.hpp:186
double submerge
Definition: frame.hpp:59
double offset
Definition: frame.hpp:58
Definition: display.hpp:44
int get_location_x(const map_location &loc) const
Functions to get the on-screen positions of hexes.
Definition: display.cpp:621
#define ERR_NG
Definition: frame.cpp:25
ORIENTATION
Definition: halo.hpp:35
const uint8_t ALPHA_OPAQUE
Definition: color.hpp:48
progressive_int directional_y_
Definition: frame.hpp:190
void adjust_surface_alpha(surface &surf, fixed_t amount)
Definition: utils.cpp:1175
iterator end() const
Definition: hex_rect.hpp:105
boost::tribool auto_vflip_
Definition: frame.hpp:130
this module manages the cache of images.
Definition: image.cpp:102
Standard logging facilities (interface).
std::string halo_mod_
Definition: frame.hpp:176
frame_builder & duration(const int duration)
Allow easy chained modifications.
Definition: frame.cpp:162
std::string image_diagonal_
Definition: frame.hpp:109
boost::tribool auto_hflip_
Definition: frame.hpp:131
progressive_double offset_
Definition: frame.hpp:185
#define e
frame_builder & highlight(const std::string &highlight)
Definition: frame.cpp:175
std::string halo_x_
Definition: frame.hpp:112
std::string x_
Definition: frame.hpp:125
boost::optional< color_t > blend_with_
Definition: frame.hpp:119
bool is_void() const
Definition: image.hpp:97
progressive_string halo_
Definition: frame.hpp:172
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
progressive_int y_
Definition: frame.hpp:188
std::string image_mod_
Definition: frame.hpp:110
std::shared_ptr< halo_record > handle
Definition: halo.hpp:31
boost::tribool primary_frame
Definition: frame.hpp:68
#define ERR_DP
frame_builder & text(const std::string &text, const color_t text_color)
Definition: frame.cpp:146
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:41
bool does_not_change() const
Definition: frame.cpp:268
Small wrapper that queries metadata about the provided texture.
Definition: texture.hpp:44
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
const 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:810
texture get_texture(const image::locator &i_locator, TYPE type)
Definition: image.cpp:1483
progressive_pair< double > progressive_double
static game_display * get_singleton()
std::string submerge_
Definition: frame.hpp:124
std::string sound_
Definition: frame.hpp:115
boost::tribool auto_vflip_
Definition: frame.hpp:192
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:557