The Battle for Wesnoth  1.15.0-dev
animation.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 https://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/animation.hpp"
16 
17 #include "display.hpp"
18 #include "filter_context.hpp"
19 #include "map/map.hpp"
20 #include "play_controller.hpp"
21 #include "resources.hpp"
22 #include "color.hpp"
23 #include "units/unit.hpp"
25 #include "units/filter.hpp"
26 #include "variable.hpp"
27 #include "random.hpp"
28 
29 #include <algorithm>
30 
31 static std::string get_heal_sound(const config& cfg)
32 {
33  return cfg["healed_sound"].empty() ? "heal.wav" : cfg["healed_sound"].str();
34 }
35 
37 {
39  : attributes()
40  , children()
41  {}
42 
43  config merge() const
44  {
45  config result = attributes;
47  result.add_child(i->key, i->cfg);
48  }
49 
50  return result;
51  }
52 
54  std::vector<config::const_all_children_iterator> children;
55 };
56 
57 typedef std::list<animation_branch> animation_branches;
58 
60 {
62  : itors(cfg.all_children_range()), branches(1), parent(nullptr)
63  {
64  branches.back().attributes.merge_attributes(cfg);
65  }
66 
68  : itors(cfg.all_children_range()), branches(p->branches), parent(p)
69  {
70  // If similar 'if' condition in parent branches, we need to
71  // cull the branches where there are partial matches.
72  // Hence the need to check if the condition has come up before.
73  // Also, the attributes are merged here between branches.
74  bool previously_hits_set = false;
75  bool previously_direction_set = false;
76  bool previously_terrain_set = false;
77  bool previously_value_set = false;
78  bool previously_value_2nd_set = false;
79 
80  const std::string s_cfg_hits = cfg["hits"];
81  const std::string s_cfg_direction = cfg["direction"];
82  const std::string s_cfg_terrain = cfg["terrain_types"];
83  const std::string s_cfg_value = cfg["value"];
84  const std::string s_cfg_value_2nd = cfg["value_2nd"];
85 
86  for(const auto& branch : branches) {
87  const std::string s_branch_hits = branch.attributes["hits"];
88  const std::string s_branch_direction = branch.attributes["direction"];
89  const std::string s_branch_terrain = branch.attributes["terrain_types"];
90  const std::string s_branch_value = branch.attributes["value"];
91  const std::string s_branch_value_2nd = branch.attributes["value_second"];
92 
93  if(!s_branch_hits.empty() && s_branch_hits == s_cfg_hits) {
94  previously_hits_set = true;
95  }
96 
97  if(!s_branch_direction.empty() && s_branch_direction == s_cfg_direction) {
98  previously_direction_set = true;
99  }
100 
101  if(!s_branch_terrain.empty() && s_branch_terrain == s_cfg_terrain) {
102  previously_terrain_set = true;
103  }
104 
105  if(!s_branch_value.empty() && s_branch_value == s_cfg_value) {
106  previously_value_set = true;
107  }
108 
109  if(!s_branch_value_2nd.empty() && s_branch_value_2nd == s_cfg_value_2nd) {
110  previously_value_2nd_set = true;
111  }
112  }
113 
114  // Merge all frames that have new matches and prune any impossible
115  // matches, e.g. hits='yes' and hits='no'
116  for(auto iter = branches.begin(); iter != branches.end(); /* nothing */) {
117  const std::string s_branch_hits = (*iter).attributes["hits"];
118  const std::string s_branch_direction = (*iter).attributes["direction"];
119  const std::string s_branch_terrain = (*iter).attributes["terrain_types"];
120  const std::string s_branch_value = (*iter).attributes["value"];
121  const std::string s_branch_value_2nd = (*iter).attributes["value_second"];
122 
123  const bool hits_match = (previously_hits_set && s_branch_hits != s_cfg_hits);
124  const bool direction_match = (previously_direction_set && s_branch_direction != s_cfg_direction);
125  const bool terrain_match = (previously_terrain_set && s_branch_terrain != s_cfg_terrain);
126  const bool value_match = (previously_value_set && s_branch_value != s_cfg_value);
127  const bool value_2nd_match = (previously_value_2nd_set && s_branch_value_2nd != s_cfg_value_2nd);
128 
129  if((!previously_hits_set || hits_match) &&
130  (!previously_direction_set || direction_match) &&
131  (!previously_terrain_set || terrain_match) &&
132  (!previously_value_set || value_match) &&
133  (!previously_value_2nd_set || value_2nd_match) &&
134  (hits_match || direction_match || terrain_match || value_match || value_2nd_match))
135  {
136  branches.erase(iter++);
137  } else {
138  (*iter).attributes.merge_attributes(cfg);
139  ++iter;
140  }
141  }
142 
143  // Then we prune all parent branches with similar matches as they
144  // now will not have the full frame list
145  for(auto iter = parent->branches.begin(); iter != parent->branches.end(); /* nothing */) {
146  const std::string s_branch_hits = (*iter).attributes["hits"];
147  const std::string s_branch_direction = (*iter).attributes["direction"];
148  const std::string s_branch_terrain = (*iter).attributes["terrain_types"];
149  const std::string s_branch_value = (*iter).attributes["value"];
150  const std::string s_branch_value_2nd = (*iter).attributes["value_second"];
151 
152  const bool hits_match = (previously_hits_set && s_branch_hits == s_cfg_hits);
153  const bool direction_match = (previously_direction_set && s_branch_direction == s_cfg_direction);
154  const bool terrain_match = (previously_terrain_set && s_branch_terrain == s_cfg_terrain);
155  const bool value_match = (previously_value_set && s_branch_value == s_cfg_value);
156  const bool value_2nd_match = (previously_value_2nd_set && s_branch_value_2nd == s_cfg_value_2nd);
157 
158  if((!previously_hits_set || hits_match) &&
159  (!previously_direction_set || direction_match) &&
160  (!previously_terrain_set || terrain_match) &&
161  (!previously_value_set || value_match) &&
162  (!previously_value_2nd_set || value_2nd_match) &&
163  (hits_match || direction_match || terrain_match || value_match || value_2nd_match))
164  {
165  parent->branches.erase(iter++);
166  } else {
167  ++iter;
168  }
169  }
170  }
171 
173 
176 };
177 
178 static void prepare_single_animation(const config& anim_cfg, animation_branches& expanded_anims)
179 {
180  /* The anim_cursor holds the current parsing through the config and the branches hold the data
181  * that will be interpreted as the actual animation. The branches store the config attributes
182  * for each block and the children of those branches make up all the 'frame', 'missile_frame',
183  * etc. individually (so 2 instances of 'frame' would be stored as 2 children).
184  */
185  std::list<animation_cursor> anim_cursors;
186  anim_cursors.emplace_back(anim_cfg);
187 
188  while(!anim_cursors.empty()) {
189  animation_cursor& ac = anim_cursors.back();
190 
191  // Reached end of sub-tag config block
192  if(ac.itors.empty()) {
193  if(!ac.parent) break;
194 
195  // Merge all the current branches into the parent.
196  ac.parent->branches.splice(ac.parent->branches.end(), ac.branches);
197  anim_cursors.pop_back();
198  continue;
199  }
200 
201  if(ac.itors.front().key != "if") {
202  // Append current config object to all the branches in scope.
203  for(animation_branch &ab : ac.branches) {
204  ab.children.push_back(ac.itors.begin());
205  }
206 
207  ac.itors.pop_front();
208  continue;
209  }
210 
211  int count = 0;
212  do {
213  // Copies the current branches to each cursor created for the conditional clauses.
214  // Merge attributes of the clause into them.
215  anim_cursors.emplace_back(ac.itors.front().cfg, &ac);
216  ac.itors.pop_front();
217  ++count;
218  } while (!ac.itors.empty() && ac.itors.front().key == "else");
219 
220  if(count > 1) {
221  // When else statements present, clear all branches before 'if'
222  ac.branches.clear();
223  }
224  }
225 
226 #if 0
227  // Debug aid
228  for(animation_branch& ab : anim_cursors.back().branches) {
229  std::cout << "--branch--\n" << ab.attributes;
230  for(config::all_children_iterator &ci : ab.children) {
231  std::cout << "--branchcfg--\n" << ci->cfg;
232  }
233  std::cout << "\n";
234  }
235 #endif
236 
237  // Create the config object describing each branch.
238  assert(anim_cursors.size() == 1);
239  animation_cursor& ac = anim_cursors.back();
240  expanded_anims.splice(expanded_anims.end(), ac.branches, ac.branches.begin(), ac.branches.end());
241 }
242 
243 static animation_branches prepare_animation(const config& cfg, const std::string& animation_tag)
244 {
245  animation_branches expanded_animations;
246  for(const config &anim : cfg.child_range(animation_tag)) {
247  prepare_single_animation(anim, expanded_animations);
248  }
249 
250  return expanded_animations;
251 }
252 
254  const unit_frame& frame, const std::string& event, const int variation, const frame_builder& builder)
255  : terrain_types_()
256  , unit_filter_()
257  , secondary_unit_filter_()
258  , directions_()
259  , frequency_(0)
260  , base_score_(variation)
261  , event_(utils::split(event))
262  , value_()
263  , primary_attack_filter_()
264  , secondary_attack_filter_()
265  , hits_()
266  , value2_()
267  , sub_anims_()
268  , unit_anim_(start_time,builder)
269  , src_()
270  , dst_()
271  , invalidated_(false)
272  , play_offscreen_(true)
273  , overlaped_hex_()
274 {
275  add_frame(frame.duration(),frame,!frame.does_not_change());
276 }
277 
278 unit_animation::unit_animation(const config& cfg,const std::string& frame_string )
279  : terrain_types_(t_translation::read_list(cfg["terrain_type"].str()))
280  , unit_filter_()
282  , directions_()
283  , frequency_(cfg["frequency"])
284  , base_score_(cfg["base_score"])
285  , event_()
286  , value_()
289  , hits_()
290  , value2_()
291  , sub_anims_()
292  , unit_anim_(cfg,frame_string)
293  , src_()
294  , dst_()
295  , invalidated_(false)
296  , play_offscreen_(true)
297  , overlaped_hex_()
298 {
299  //if(!cfg["debug"].empty()) printf("DEBUG WML: FINAL\n%s\n\n",cfg.debug().c_str());
300 
301  for(const config::any_child& fr : cfg.all_children_range()) {
302  if(fr.key == frame_string) {
303  continue;
304  }
305 
306  if(fr.key.find("_frame", fr.key.size() - 6) == std::string::npos) {
307  continue;
308  }
309 
310  if(sub_anims_.find(fr.key) != sub_anims_.end()) {
311  continue;
312  }
313 
314  sub_anims_[fr.key] = particle(cfg, fr.key.substr(0, fr.key.size() - 5));
315  }
316 
317  event_ = utils::split(cfg["apply_to"]);
318 
319  const std::vector<std::string>& my_directions = utils::split(cfg["direction"]);
320  for(const auto& direction : my_directions) {
322  directions_.push_back(d);
323  }
324 
325  /*const filter_context* fc = game_display::get_singleton();
326  if(!fc) {
327  // This is a pointer to the gamestate. Would prefer to tie unit animations only to the display, but for now this
328  // is an acceptable fallback. It seems to be relevant because when a second game is created, it seems that the
329  // game_display is null at the time that units are being constructed, and hence at the time that this code is running.
330  // A different solution might be to delay the team_builder stage 2 call until after the gui is initialized. Note that
331  // the current set up could conceivably cause problems with the editor, iirc it doesn't initialize a filter context.
332  fc = resources::filter_con;
333  assert(fc);
334  }*/
335 
336  for(const config& filter : cfg.child_range("filter")) {
337  unit_filter_.push_back(filter);
338  }
339 
340  for(const config& filter : cfg.child_range("filter_second")) {
341  secondary_unit_filter_.push_back(filter);
342  }
343 
344  for(const auto& v : utils::split(cfg["value"])) {
345  value_.push_back(atoi(v.c_str()));
346  }
347 
348  for(const auto& h : utils::split(cfg["hits"])) {
349  if(h == "yes" || h == "hit") {
350  hits_.push_back(hit_type::HIT);
351  }
352 
353  if(h == "no" || h == "miss") {
354  hits_.push_back(hit_type::MISS);
355  }
356 
357  if(h == "yes" || h == "kill" ) {
358  hits_.push_back(hit_type::KILL);
359  }
360  }
361 
362  for(const auto& v2 : utils::split(cfg["value_second"])) {
363  value2_.push_back(atoi(v2.c_str()));
364  }
365 
366  for(const config& filter : cfg.child_range("filter_attack")) {
367  primary_attack_filter_.push_back(filter);
368  }
369 
370  for(const config& filter : cfg.child_range("filter_second_attack")) {
371  secondary_attack_filter_.push_back(filter);
372  }
373 
374  play_offscreen_ = cfg["offscreen"].to_bool(true);
375 }
376 
377 int unit_animation::matches(const display& disp, const map_location& loc, const map_location& second_loc,
378  const unit* my_unit, const std::string& event, const int value, hit_type hit, const_attack_ptr attack,
379  const_attack_ptr second_attack, int value2) const
380 {
381  int result = base_score_;
382 
383  if(!event.empty() && !event_.empty()) {
384  if(std::find(event_.begin(), event_.end(), event) == event_.end()) {
385  return MATCH_FAIL;
386  }
387 
388  result++;
389  }
390 
391  if(!terrain_types_.empty()) {
393  return MATCH_FAIL;
394  }
395 
396  result++;
397  }
398 
399  if(!value_.empty()) {
400  if(std::find(value_.begin(), value_.end(), value) == value_.end()) {
401  return MATCH_FAIL;
402  }
403 
404  result++;
405  }
406 
407  if(my_unit) {
408  if(!directions_.empty()) {
409  if(std::find(directions_.begin(), directions_.end(), my_unit->facing()) == directions_.end()) {
410  return MATCH_FAIL;
411  }
412 
413  result++;
414  }
415 
416  for(const auto& filter : unit_filter_) {
417  unit_filter f{ vconfig(filter) };
418  if(!f(*my_unit, loc)) return MATCH_FAIL;
419  ++result;
420  }
421 
422  if(!secondary_unit_filter_.empty()) {
423  unit_map::const_iterator unit = disp.get_units().find(second_loc);
424  if(!unit.valid()) {
425  return MATCH_FAIL;
426  }
427 
428  for(const config& c : secondary_unit_filter_) {
429  unit_filter f{ vconfig(c) };
430  if(!f(*unit, second_loc)) return MATCH_FAIL;
431  result++;
432  }
433  }
434  } else if(!unit_filter_.empty()) {
435  return MATCH_FAIL;
436  }
437 
438  if(frequency_ && !(randomness::rng::default_instance().get_random_int(0, frequency_-1))) {
439  return MATCH_FAIL;
440  }
441 
442  if(!hits_.empty()) {
443  if(std::find(hits_.begin(),hits_.end(),hit) == hits_.end()) {
444  return MATCH_FAIL;
445  }
446 
447  result ++;
448  }
449 
450  if(!value2_.empty()) {
451  if(std::find(value2_.begin(),value2_.end(),value2) == value2_.end()) {
452  return MATCH_FAIL;
453  }
454 
455  result ++;
456  }
457 
458  if(!attack) {
459  if(!primary_attack_filter_.empty()) {
460  return MATCH_FAIL;
461  }
462  }
463 
464  for(const auto& iter : primary_attack_filter_) {
465  if(!attack->matches_filter(iter)) return MATCH_FAIL;
466  result++;
467  }
468 
469  if(!second_attack) {
470  if(!secondary_attack_filter_.empty()) {
471  return MATCH_FAIL;
472  }
473  }
474 
475  for(const auto& iter : secondary_attack_filter_) {
476  if(!second_attack->matches_filter(iter)) return MATCH_FAIL;
477  result++;
478  }
479 
480  return result;
481 }
482 
483 void unit_animation::fill_initial_animations(std::vector<unit_animation>& animations, const config& cfg)
484 {
485  add_anims(animations, cfg);
486 
487  std::vector<unit_animation> animation_base;
488  for(const auto& anim : animations) {
489  if(std::find(anim.event_.begin(), anim.event_.end(), "default") != anim.event_.end()) {
490  animation_base.push_back(anim);
491  animation_base.back().base_score_ += unit_animation::DEFAULT_ANIM;
492  animation_base.back().event_.clear();
493  }
494  }
495 
496  const std::string default_image = cfg["image"];
497 
498  if(animation_base.empty()) {
499  animation_base.push_back(unit_animation(0, frame_builder().image(default_image).duration(1), "", unit_animation::DEFAULT_ANIM));
500  }
501 
502  animations.push_back(unit_animation(0, frame_builder().image(default_image).duration(1), "_disabled_", 0));
503  animations.push_back(unit_animation(0,
504  frame_builder().image(default_image).duration(300).blend("0.0~0.3:100,0.3~0.0:200", {255,255,255}),
505  "_disabled_selected_", 0));
506 
507  for(const auto& base : animation_base) {
508  animations.push_back(base);
509  animations.back().event_ = { "standing" };
510  animations.back().play_offscreen_ = false;
511 
512  animations.push_back(base);
513  animations.back().event_ = { "_ghosted_" };
514  animations.back().unit_anim_.override(0, animations.back().unit_anim_.get_animation_duration(),particle::UNSET,"0.9", "", {0,0,0}, "", "", "~GS()");
515 
516  animations.push_back(base);
517  animations.back().event_ = { "_disabled_ghosted_" };
518  animations.back().unit_anim_.override(0, 1, particle::UNSET, "0.4", "", {0,0,0}, "", "", "~GS()");
519 
520  animations.push_back(base);
521  animations.back().event_ = { "selected" };
522  animations.back().unit_anim_.override(0, 300, particle::UNSET, "", "0.0~0.3:100,0.3~0.0:200", {255,255,255});
523 
524  animations.push_back(base);
525  animations.back().event_ = { "recruited" };
526  animations.back().unit_anim_.override(0, 600, particle::NO_CYCLE, "0~1:600");
527 
528  animations.push_back(base);
529  animations.back().event_ = { "levelin" };
530  animations.back().unit_anim_.override(0, 600, particle::NO_CYCLE, "", "1~0:600", {255,255,255});
531 
532  animations.push_back(base);
533  animations.back().event_ = { "levelout" };
534  animations.back().unit_anim_.override(0, 600, particle::NO_CYCLE, "", "0~1:600,1", {255,255,255});
535 
536  animations.push_back(base);
537  animations.back().event_ = { "pre_movement" };
538  animations.back().unit_anim_.override(0, 1, particle::NO_CYCLE);
539 
540  animations.push_back(base);
541  animations.back().event_ = { "post_movement" };
542  animations.back().unit_anim_.override(0, 1, particle::NO_CYCLE);
543 
544  animations.push_back(base);
545  animations.back().event_ = { "movement" };
546  animations.back().unit_anim_.override(0, 200,
547  particle::NO_CYCLE, "", "", {0,0,0}, "0~1:200", std::to_string(display::LAYER_UNIT_MOVE_DEFAULT - display::LAYER_UNIT_FIRST));
548 
549  animations.push_back(base);
550  animations.back().event_ = { "defend" };
551  animations.back().unit_anim_.override(0, animations.back().unit_anim_.get_animation_duration(),
552  particle::NO_CYCLE, "", "0.0,0.5:75,0.0:75,0.5:75,0.0", {255,0,0});
553  animations.back().hits_.push_back(hit_type::HIT);
554  animations.back().hits_.push_back(hit_type::KILL);
555 
556  animations.push_back(base);
557  animations.back().event_ = { "defend" };
558 
559  animations.push_back(base);
560  animations.back().event_ = { "attack" };
561  animations.back().unit_anim_.override(-150, 300, particle::NO_CYCLE, "", "", {0,0,0}, "0~0.6:150,0.6~0:150", std::to_string(display::LAYER_UNIT_MOVE_DEFAULT-display::LAYER_UNIT_FIRST));
562  animations.back().primary_attack_filter_.emplace_back("range", "melee");
563 
564  animations.push_back(base);
565  animations.back().event_ = { "attack" };
566  animations.back().unit_anim_.override(-150, 150, particle::NO_CYCLE);
567  animations.back().primary_attack_filter_.emplace_back("range", "ranged");
568 
569  animations.push_back(base);
570  animations.back().event_ = { "death" };
571  animations.back().unit_anim_.override(0, 600, particle::NO_CYCLE, "1~0:600");
572  animations.back().sub_anims_["_death_sound"] = particle();
573  animations.back().sub_anims_["_death_sound"].add_frame(1, frame_builder().sound(cfg["die_sound"]), true);
574 
575  animations.push_back(base);
576  animations.back().event_ = { "victory" };
577  animations.back().unit_anim_.override(0, animations.back().unit_anim_.get_animation_duration(), particle::CYCLE);
578 
579  animations.push_back(base);
580  animations.back().unit_anim_.override(0, 150, particle::NO_CYCLE, "1~0:150");
581  animations.back().event_ = { "pre_teleport" };
582 
583  animations.push_back(base);
584  animations.back().unit_anim_.override(0, 150, particle::NO_CYCLE, "0~1:150,1");
585  animations.back().event_ = { "post_teleport" };
586 
587  animations.push_back(base);
588  animations.back().event_ = { "healing" };
589 
590  animations.push_back(base);
591  animations.back().event_ = { "healed" };
592  animations.back().unit_anim_.override(0, 300, particle::NO_CYCLE, "", "0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30", {255,255,255});
593 
594  const std::string healed_sound = get_heal_sound(cfg);
595 
596  animations.back().sub_anims_["_healed_sound"].add_frame(1, frame_builder().sound(healed_sound), true);
597 
598  animations.push_back(base);
599  animations.back().event_ = { "poisoned" };
600  animations.back().unit_anim_.override(0, 300, particle::NO_CYCLE, "", "0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30", {0,255,0});
601  animations.back().sub_anims_["_poison_sound"] = particle();
602  animations.back().sub_anims_["_poison_sound"].add_frame(1, frame_builder().sound(game_config::sounds::status::poisoned), true);
603  }
604 }
605 
606 static void add_simple_anim(std::vector<unit_animation>& animations,
607  const config& cfg, char const* tag_name, char const* apply_to,
609  bool offscreen = true)
610 {
611  for(const animation_branch& ab : prepare_animation(cfg, tag_name)) {
612  config anim = ab.merge();
613  anim["apply_to"] = apply_to;
614 
615  if(!offscreen) {
616  config::attribute_value& v = anim["offscreen"];
617  if(v.empty()) v = false;
618  }
619 
620  config::attribute_value& v = anim["layer"];
621  if(v.empty()) v = layer - display::LAYER_UNIT_FIRST;
622 
623  animations.emplace_back(anim);
624  }
625 }
626 
627 void unit_animation::add_anims( std::vector<unit_animation> & animations, const config & cfg)
628 {
629  for(const animation_branch& ab : prepare_animation(cfg, "animation")) {
630  animations.emplace_back(ab.merge());
631  }
632 
633  const int default_layer = display::LAYER_UNIT_DEFAULT - display::LAYER_UNIT_FIRST;
636 
637  add_simple_anim(animations, cfg, "resistance_anim", "resistance");
638  add_simple_anim(animations, cfg, "leading_anim", "leading");
639  add_simple_anim(animations, cfg, "recruit_anim", "recruited");
640  add_simple_anim(animations, cfg, "recruiting_anim", "recruiting");
641  add_simple_anim(animations, cfg, "idle_anim", "idling", display::LAYER_UNIT_DEFAULT, false);
642  add_simple_anim(animations, cfg, "levelin_anim", "levelin");
643  add_simple_anim(animations, cfg, "levelout_anim", "levelout");
644 
645  for(const animation_branch& ab : prepare_animation(cfg, "standing_anim")) {
646  config anim = ab.merge();
647  anim["apply_to"] = "standing";
648  anim["cycles"] = true;
649 
650  // Add cycles to all frames within a standing animation block
651  for(config::const_all_children_iterator ci : ab.children) {
652  std::string sub_frame_name = ci->key;
653  std::size_t pos = sub_frame_name.find("_frame");
654  if(pos != std::string::npos) {
655  anim[sub_frame_name.substr(0, pos) + "_cycles"] = true;
656  }
657  }
658 
659  if(anim["layer"].empty()) {
660  anim["layer"] = default_layer;
661  }
662 
663  if(anim["offscreen"].empty()) {
664  anim["offscreen"] = false;
665  }
666 
667  animations.emplace_back(anim);
668  }
669 
670  // Standing animations are also used as default animations
671  for(const animation_branch& ab : prepare_animation(cfg, "standing_anim")) {
672  config anim = ab.merge();
673  anim["apply_to"] = "default";
674  anim["cycles"] = true;
675 
676  for(config::const_all_children_iterator ci : ab.children) {
677  std::string sub_frame_name = ci->key;
678  std::size_t pos = sub_frame_name.find("_frame");
679  if(pos != std::string::npos) {
680  anim[sub_frame_name.substr(0, pos) + "_cycles"] = true;
681  }
682  }
683 
684  if(anim["layer"].empty()) {
685  anim["layer"] = default_layer;
686  }
687 
688  if(anim["offscreen"].empty()) {
689  anim["offscreen"] = false;
690  }
691 
692  animations.emplace_back(anim);
693  }
694 
695  for(const animation_branch& ab : prepare_animation(cfg, "healing_anim")) {
696  config anim = ab.merge();
697  anim["apply_to"] = "healing";
698  anim["value"] = anim["damage"];
699 
700  if(anim["layer"].empty()) {
701  anim["layer"] = default_layer;
702  }
703 
704  animations.emplace_back(anim);
705  }
706 
707  for(const animation_branch& ab : prepare_animation(cfg, "healed_anim")) {
708  config anim = ab.merge();
709  anim["apply_to"] = "healed";
710  anim["value"] = anim["healing"];
711 
712  if(anim["layer"].empty()) {
713  anim["layer"] = default_layer;
714  }
715 
716  animations.emplace_back(anim);
717  animations.back().sub_anims_["_healed_sound"] = particle();
718 
719  const std::string healed_sound = get_heal_sound(cfg);
720  animations.back().sub_anims_["_healed_sound"].add_frame(1,frame_builder().sound(healed_sound),true);
721  }
722 
723  for(const animation_branch &ab : prepare_animation(cfg, "poison_anim")) {
724  config anim = ab.merge();
725  anim["apply_to"] = "poisoned";
726  anim["value"] = anim["damage"];
727 
728  if(anim["layer"].empty()) {
729  anim["layer"] = default_layer;
730  }
731 
732  animations.emplace_back(anim);
733  animations.back().sub_anims_["_poison_sound"] = particle();
734  animations.back().sub_anims_["_poison_sound"].add_frame(1,frame_builder().sound(game_config::sounds::status::poisoned),true);
735  }
736 
737  add_simple_anim(animations, cfg, "pre_movement_anim", "pre_movement", display::LAYER_UNIT_MOVE_DEFAULT);
738 
739  for(const animation_branch& ab : prepare_animation(cfg, "movement_anim")) {
740  config anim = ab.merge();
741  anim["apply_to"] = "movement";
742 
743  if(anim["offset"].empty()) {
744  anim["offset"] = "0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,";
745  }
746 
747  if(anim["layer"].empty()) {
748  anim["layer"] = move_layer;
749  }
750 
751  animations.emplace_back(anim);
752  }
753 
754  add_simple_anim(animations, cfg, "post_movement_anim", "post_movement", display::LAYER_UNIT_MOVE_DEFAULT);
755 
756  for(const animation_branch& ab : prepare_animation(cfg, "defend")) {
757  config anim = ab.merge();
758  anim["apply_to"] = "defend";
759 
760  if(anim["layer"].empty()) {
761  anim["layer"] = default_layer;
762  }
763 
764  if(!anim["damage"].empty() && anim["value"].empty()) {
765  anim["value"] = anim["damage"];
766  }
767 
768  if(anim["hits"].empty()) {
769  anim["hits"] = false;
770  animations.emplace_back(anim);
771  animations.back().base_score_--; //so default doesn't interfere with 'if' block
772 
773  anim["hits"] = true;
774  animations.emplace_back(anim);
775  animations.back().base_score_--;
776 
777  image::locator image_loc = animations.back().get_last_frame().end_parameters().image;
778  animations.back().add_frame(225, frame_builder()
779  .image(image_loc.get_filename()+image_loc.get_modifications())
780  .duration(225)
781  .blend("0.0,0.5:75,0.0:75,0.5:75,0.0", {255,0,0}));
782  } else {
783  for(const std::string& hit_type : utils::split(anim["hits"])) {
784  config tmp = anim;
785  tmp["hits"] = hit_type;
786 
787  animations.emplace_back(tmp);
788 
789  image::locator image_loc = animations.back().get_last_frame().end_parameters().image;
790  if(hit_type == "yes" || hit_type == "hit" || hit_type=="kill") {
791  animations.back().add_frame(225, frame_builder()
792  .image(image_loc.get_filename() + image_loc.get_modifications())
793  .duration(225)
794  .blend("0.0,0.5:75,0.0:75,0.5:75,0.0", {255,0,0}));
795  }
796  }
797  }
798  }
799 
800  add_simple_anim(animations, cfg, "draw_weapon_anim", "draw_weapon", display::LAYER_UNIT_MOVE_DEFAULT);
801  add_simple_anim(animations, cfg, "sheath_weapon_anim", "sheath_weapon", display::LAYER_UNIT_MOVE_DEFAULT);
802 
803  for(const animation_branch& ab : prepare_animation(cfg, "attack_anim")) {
804  config anim = ab.merge();
805  anim["apply_to"] = "attack";
806 
807  if(anim["layer"].empty()) {
808  anim["layer"] = move_layer;
809  }
810 
811  config::const_child_itors missile_fs = anim.child_range("missile_frame");
812  if(anim["offset"].empty() && missile_fs.empty()) {
813  anim["offset"] ="0~0.6,0.6~0";
814  }
815 
816  if(!missile_fs.empty()) {
817  if(anim["missile_offset"].empty()) {
818  anim["missile_offset"] = "0~0.8";
819  }
820 
821  if(anim["missile_layer"].empty()) {
822  anim["missile_layer"] = missile_layer;
823  }
824 
825  config tmp;
826  tmp["duration"] = 1;
827 
828  anim.add_child("missile_frame", tmp);
829  anim.add_child_at("missile_frame", tmp, 0);
830  }
831 
832  animations.emplace_back(anim);
833  }
834 
835  for(const animation_branch& ab : prepare_animation(cfg, "death")) {
836  config anim = ab.merge();
837  anim["apply_to"] = "death";
838 
839  if(anim["layer"].empty()) {
840  anim["layer"] = default_layer;
841  }
842 
843  animations.emplace_back(anim);
844  image::locator image_loc = animations.back().get_last_frame().end_parameters().image;
845 
846  animations.back().add_frame(600, frame_builder()
847  .image(image_loc.get_filename()+image_loc.get_modifications())
848  .duration(600)
849  .highlight("1~0:600"));
850 
851  if(!cfg["die_sound"].empty()) {
852  animations.back().sub_anims_["_death_sound"] = particle();
853  animations.back().sub_anims_["_death_sound"].add_frame(1,frame_builder().sound(cfg["die_sound"]),true);
854  }
855  }
856 
857  add_simple_anim(animations, cfg, "victory_anim", "victory");
858 
859  for(const animation_branch& ab : prepare_animation(cfg, "extra_anim")) {
860  config anim = ab.merge();
861  anim["apply_to"] = anim["flag"];
862 
863  if(anim["layer"].empty()) {
864  anim["layer"] = default_layer;
865  }
866 
867  animations.emplace_back(anim);
868  }
869 
870  for(const animation_branch& ab : prepare_animation(cfg, "teleport_anim")) {
871  config anim = ab.merge();
872  if(anim["layer"].empty()) {
873  anim["layer"] = default_layer;
874  }
875 
876  anim["apply_to"] = "pre_teleport";
877  animations.emplace_back(anim);
878  animations.back().unit_anim_.set_end_time(0);
879 
880  anim["apply_to"] ="post_teleport";
881  animations.emplace_back(anim);
882  animations.back().unit_anim_.remove_frames_until(0);
883  }
884 }
885 
887  , int duration
888  , const cycle_state cycles
889  , const std::string& highlight
890  , const std::string& blend_ratio
891  , color_t blend_color
892  , const std::string& offset
893  , const std::string& layer
894  , const std::string& modifiers)
895 {
896  set_begin_time(start_time);
897  parameters_.override(duration,highlight,blend_ratio,blend_color,offset,layer,modifiers);
898 
899  if(cycles == CYCLE) {
900  cycles_=true;
901  } else if(cycles==NO_CYCLE) {
902  cycles_=false;
903  }
904 
905  if(get_animation_duration() < duration) {
906  add_frame(duration -get_animation_duration(), get_last_frame());
907  } else if(get_animation_duration() > duration) {
908  set_end_time(duration);
909  }
910 }
911 
913 {
914  if(animated<unit_frame>::need_update()) return true;
915  if(get_current_frame().need_update()) return true;
916  if(parameters_.need_update()) return true;
917  return false;
918 }
919 
921 {
922  return get_current_frame_begin_time() != last_frame_begin_time_;
923 }
924 
925 unit_animation::particle::particle(const config& cfg, const std::string& frame_string)
926  : animated<unit_frame>()
927  , accelerate(true)
928  , parameters_()
929  , halo_id_()
930  , last_frame_begin_time_(0)
931  , cycles_(false)
932 {
933  starting_frame_time_ = INT_MAX;
934 
935  config::const_child_itors range = cfg.child_range(frame_string + "frame");
936  if(!range.empty() && cfg[frame_string + "start_time"].empty()) {
937  for(const config& frame : range) {
938  starting_frame_time_ = std::min(starting_frame_time_, frame["begin"].to_int());
939  }
940  } else {
941  starting_frame_time_ = cfg[frame_string + "start_time"];
942  }
943 
944  for(const config& frame : range) {
945  unit_frame tmp_frame(frame);
946  add_frame(tmp_frame.duration(), tmp_frame, !tmp_frame.does_not_change());
947  }
948 
949  cycles_ = cfg[frame_string + "cycles"].to_bool(false);
951 
953  force_change();
954  }
955 }
956 
958 {
959  if(unit_anim_.need_update()) return true;
960  for(const auto& anim : sub_anims_) {
961  if(anim.second.need_update()) return true;
962  }
963 
964  return false;
965 }
966 
968 {
969  if(!play_offscreen_) {
970  return false;
971  }
972 
973  if(unit_anim_.need_minimal_update()) return true;
974 
975  for(const auto& anim : sub_anims_) {
976  if(anim.second.need_minimal_update()) return true;
977  }
978 
979  return false;
980 }
981 
983 {
984  if(!unit_anim_.animation_finished()) return false;
985  for(const auto& anim : sub_anims_) {
986  if(!anim.second.animation_finished()) return false;
987  }
988 
989  return true;
990 }
991 
993 {
994  if(!unit_anim_.animation_finished_potential()) return false;
995  for(const auto& anim : sub_anims_) {
996  if(!anim.second.animation_finished_potential()) return false;
997  }
998 
999  return true;
1000 }
1001 
1003 {
1004  double acceleration = unit_anim_.accelerate ? display::get_singleton()->turbo_speed() : 1.0;
1005  unit_anim_.update_last_draw_time(acceleration);
1006  for(auto& anim : sub_anims_) {
1007  anim.second.update_last_draw_time(acceleration);
1008  }
1009 }
1010 
1012 {
1013  int result = unit_anim_.get_end_time();
1014  for(const auto& anim : sub_anims_) {
1015  result = std::max<int>(result, anim.second.get_end_time());
1016  }
1017 
1018  return result;
1019 }
1020 
1022 {
1023  int result = unit_anim_.get_begin_time();
1024  for(const auto& anim : sub_anims_) {
1025  result = std::min<int>(result, anim.second.get_begin_time());
1026  }
1027 
1028  return result;
1029 }
1030 
1032  , const map_location& src
1033  , const map_location& dst
1034  , const std::string& text
1035  , const color_t text_color
1036  , const bool accelerate)
1037 {
1039  src_ = src;
1040  dst_ = dst;
1041 
1042  unit_anim_.start_animation(start_time);
1043 
1044  if(!text.empty()) {
1045  particle crude_build;
1046  crude_build.add_frame(1, frame_builder());
1047  crude_build.add_frame(1, frame_builder().text(text, text_color), true);
1048  sub_anims_["_add_text"] = crude_build;
1049  }
1050 
1051  for(auto& anim : sub_anims_) {
1052  anim.second.accelerate = accelerate;
1053  anim.second.start_animation(start_time);
1054  }
1055 }
1056 
1058 {
1059  src_ = src;
1060  dst_ = dst;
1061 }
1062 
1064 {
1066 
1067  for(auto& anim : sub_anims_) {
1068  anim.second.pause_animation();
1069  }
1070 }
1071 
1073 {
1075 
1076  for(auto& anim : sub_anims_) {
1077  anim.second.restart_animation();
1078  }
1079 }
1080 
1082 {
1083  invalidated_ = false;
1084  overlaped_hex_.clear();
1085 
1086  value.primary_frame = true;
1087  unit_anim_.redraw(value,src_,dst_, halo_man);
1088 
1089  value.primary_frame = false;
1090  for(auto& anim : sub_anims_) {
1091  anim.second.redraw(value, src_, dst_, halo_man);
1092  }
1093 }
1094 
1096 {
1098 
1099  for(auto& anim : sub_anims_) {
1100  anim.second.clear_halo();
1101  }
1102 }
1103 
1105 {
1106  if(invalidated_) return false;
1107 
1108  display* disp = display::get_singleton();
1109  const bool complete_redraw = disp->tile_nearly_on_screen(src_) || disp->tile_nearly_on_screen(dst_);
1110 
1111  if(overlaped_hex_.empty()) {
1112  if(complete_redraw) {
1113  value.primary_frame = true;
1115  value.primary_frame = false;
1116 
1117  for(auto& anim : sub_anims_) {
1118  std::set<map_location> tmp = anim.second.get_overlaped_hex(value, src_, dst_);
1119  overlaped_hex_.insert(tmp.begin(), tmp.end());
1120  }
1121  } else {
1122  // Offscreen animations only invalidate their own hex, no propagation,
1123  // but we still need this to play sounds
1124  overlaped_hex_.insert(src_);
1125  }
1126  }
1127 
1128  if(complete_redraw) {
1129  if( need_update()) {
1130  disp->invalidate(overlaped_hex_);
1131  invalidated_ = true;
1132  return true;
1133  } else {
1135  return invalidated_;
1136  }
1137  } else {
1138  if(need_minimal_update()) {
1139  disp->invalidate(overlaped_hex_);
1140  invalidated_ = true;
1141  return true;
1142  } else {
1143  return false;
1144  }
1145  }
1146 }
1147 
1148 std::string unit_animation::debug() const
1149 {
1150  std::ostringstream outstream;
1151  outstream << *this;
1152  return outstream.str();
1153 }
1154 
1155 std::ostream& operator<<(std::ostream& outstream, const unit_animation& u_animation)
1156 {
1157  std::string events_string = utils::join(u_animation.event_);
1158  outstream << "[" << events_string << "]\n";
1159 
1160  outstream << "\tstart_time=" << u_animation.get_begin_time() << '\n';
1161 
1162  if(u_animation.hits_.size() > 0) {
1163  std::vector<std::string> hits;
1164  std::transform(u_animation.hits_.begin(), u_animation.hits_.end(), std::back_inserter(hits), unit_animation::hit_type::enum_to_string);
1165  outstream << "\thits=" << utils::join(hits) << '\n';
1166  }
1167 
1168  if(u_animation.directions_.size() > 0) {
1169  std::vector<std::string> dirs;
1170  std::transform(u_animation.directions_.begin(), u_animation.directions_.end(), std::back_inserter(dirs), map_location::write_direction);
1171  outstream << "\tdirections=" << utils::join(dirs) << '\n';
1172  }
1173 
1174  if(u_animation.terrain_types_.size() > 0) {
1175  outstream << "\tterrain=" << utils::join(u_animation.terrain_types_) << '\n';
1176  }
1177 
1178  if(u_animation.frequency_ > 0) outstream << "frequency=" << u_animation.frequency_ << '\n';
1179 
1180  if(u_animation.unit_filter_.size() > 0) {
1181  outstream << "[filter]\n";
1182  for(const config& cfg : u_animation.unit_filter_) {
1183  outstream << cfg.debug();
1184  }
1185 
1186  outstream << "[/filter]\n";
1187  }
1188 
1189  if(u_animation.secondary_unit_filter_.size() > 0) {
1190  outstream << "[filter_second]\n";
1191  for(const config& cfg : u_animation.secondary_unit_filter_) {
1192  outstream << cfg.debug();
1193  }
1194 
1195  outstream << "[/filter_second]\n";
1196  }
1197 
1198  if(u_animation.primary_attack_filter_.size() > 0) {
1199  outstream << "[filter_attack]\n";
1200  for(const config cfg : u_animation.primary_attack_filter_) {
1201  outstream << cfg.debug();
1202  }
1203 
1204  outstream << "[/filter_attack]\n";
1205  }
1206 
1207  if(u_animation.secondary_attack_filter_.size() > 0) {
1208  outstream << "[filter_second_attack]\n";
1209  for(const config cfg : u_animation.secondary_attack_filter_) {
1210  outstream << cfg.debug();
1211  }
1212 
1213  outstream << "[/filter_second_attack]\n";
1214  }
1215 
1216  for(std::size_t i = 0; i < u_animation.unit_anim_.get_frames_count(); i++) {
1217  outstream << "\t[frame]\n";
1218  for(const std::string frame_string : u_animation.unit_anim_.get_frame(i).debug_strings()) {
1219  outstream << "\t\t" << frame_string <<"\n";
1220  }
1221  outstream << "\t[/frame]\n";
1222  }
1223 
1224  for(std::pair<std::string, unit_animation::particle> p : u_animation.sub_anims_) {
1225  for(std::size_t i = 0; i < p.second.get_frames_count(); i++) {
1226  std::string sub_frame_name = p.first;
1227  std::size_t pos = sub_frame_name.find("_frame");
1228  if(pos != std::string::npos) sub_frame_name = sub_frame_name.substr(0, pos);
1229 
1230  outstream << "\t" << sub_frame_name << "_start_time=" << p.second.get_begin_time() << '\n';
1231  outstream << "\t[" << p.first << "]\n";
1232 
1233  for(const std::string frame_string : p.second.get_frame(i).debug_strings()) {
1234  outstream << "\t\t" << frame_string << '\n';
1235  }
1236 
1237  outstream << "\t[/" << p.first << "]\n";
1238  }
1239  }
1240 
1241  outstream << "[/" << events_string << "]\n";
1242  return outstream;
1243 }
1244 
1246 {
1247  const unit_frame& current_frame = get_current_frame();
1248  const int animation_time = get_animation_time();
1249  const frame_parameters default_val = parameters_.parameters(animation_time - get_begin_time());
1250 
1251  // Everything is relative to the first frame in an attack/defense/etc. block.
1252  // so we need to check if this particular frame is due to be shown at this time
1253  bool in_scope_of_frame = (animation_time >= get_current_frame_begin_time() ? true: false);
1254  if(animation_time > get_current_frame_end_time()) in_scope_of_frame = false;
1255 
1256  // Sometimes even if the frame is not due to be shown, a frame image still must be shown.
1257  // i.e. in a defense animation that is shorter than an attack animation.
1258  // the halos should not persist though and use the 'in_scope_of_frame' variable.
1259 
1260  // For sound frames we want the first time variable set only after the frame has started.
1263  current_frame.redraw(get_current_frame_time(), true, in_scope_of_frame, src, dst, halo_id_, halo_man, default_val, value);
1264  } else {
1265  current_frame.redraw(get_current_frame_time(), false, in_scope_of_frame, src, dst, halo_id_, halo_man, default_val, value);
1266  }
1267 }
1268 
1270 {
1271  halo_id_.reset();
1272 }
1273 
1274 std::set<map_location> unit_animation::particle::get_overlaped_hex(const frame_parameters& value, const map_location& src, const map_location& dst)
1275 {
1276  const unit_frame& current_frame = get_current_frame();
1278  return current_frame.get_overlaped_hex(get_current_frame_time(), src, dst, default_val,value);
1279 }
1280 
1282 {
1283  halo_id_.reset();
1284 }
1285 
1287 {
1288  halo_id_.reset();
1292 }
1293 
1294 void unit_animator::add_animation(const unit* animated_unit
1295  , const std::string& event
1296  , const map_location &src
1297  , const map_location &dst
1298  , const int value
1299  , bool with_bars
1300  , const std::string& text
1301  , const color_t text_color
1302  , const unit_animation::hit_type hit_type
1303  , const_attack_ptr attack
1304  , const_attack_ptr second_attack
1305  , int value2)
1306 {
1307  if(!animated_unit) return;
1308 
1309  display* disp = display::get_singleton();
1310 
1311  anim_elem tmp;
1312  tmp.my_unit = unit_const_ptr(animated_unit);
1313  tmp.text = text;
1314  tmp.text_color = text_color;
1315  tmp.src = src;
1316  tmp.with_bars= with_bars;
1317  tmp.animation = animated_unit->anim_comp().choose_animation(*disp, src, event, dst, value, hit_type, attack, second_attack, value2);
1318 
1319  if(!tmp.animation) return;
1320 
1321  start_time_ = std::max<int>(start_time_, tmp.animation->get_begin_time());
1322  animated_units_.push_back(std::move(tmp));
1323 }
1324 
1325 void unit_animator::add_animation(const unit* animated_unit
1326  , const unit_animation* anim
1327  , const map_location &src
1328  , bool with_bars
1329  , const std::string& text
1330  , const color_t text_color)
1331 {
1332  if(!animated_unit) return;
1333 
1334  anim_elem tmp;
1335  tmp.my_unit = unit_const_ptr(animated_unit);
1336  tmp.text = text;
1337  tmp.text_color = text_color;
1338  tmp.src = src;
1339  tmp.with_bars = with_bars;
1340  tmp.animation = anim;
1341 
1342  if(!tmp.animation) return;
1343 
1344  start_time_ = std::max<int>(start_time_, tmp.animation->get_begin_time());
1345  animated_units_.push_back(std::move(tmp));
1346 }
1347 
1349  , const std::string& event
1350  , const map_location &src
1351  , const map_location & dst
1352  , const int value
1353  , bool with_bars
1354  , const std::string& text
1355  , const color_t text_color
1356  , const unit_animation::hit_type hit_type
1357  , const_attack_ptr attack
1358  , const_attack_ptr second_attack
1359  , int value2)
1360 {
1361  if(!animated_unit) return;
1362 
1363  display* disp = display::get_singleton();
1364  if(animated_unit->anim_comp().get_animation() &&
1365  !animated_unit->anim_comp().get_animation()->animation_finished_potential() &&
1366  animated_unit->anim_comp().get_animation()->matches(
1367  *disp, src, dst, animated_unit, event, value, hit_type, attack, second_attack, value2) > unit_animation::MATCH_FAIL)
1368  {
1369  anim_elem tmp;
1370  tmp.my_unit = unit_const_ptr(animated_unit);
1371  tmp.text = text;
1372  tmp.text_color = text_color;
1373  tmp.src = src;
1374  tmp.with_bars= with_bars;
1375  tmp.animation = nullptr;
1376 
1377  animated_units_.push_back(std::move(tmp));
1378  } else {
1379  add_animation(animated_unit,event,src,dst,value,with_bars,text,text_color,hit_type,attack,second_attack,value2);
1380  }
1381 }
1382 
1384 {
1385  int begin_time = INT_MAX;
1386 
1387  for(const auto& anim : animated_units_) {
1388  if(anim.my_unit->anim_comp().get_animation()) {
1389  if(anim.animation) {
1390  begin_time = std::min<int>(begin_time, anim.animation->get_begin_time());
1391  } else {
1392  begin_time = std::min<int>(begin_time, anim.my_unit->anim_comp().get_animation()->get_begin_time());
1393  }
1394  }
1395  }
1396 
1397  for(auto& anim : animated_units_) {
1398  if(anim.animation) {
1399  anim.my_unit->anim_comp().start_animation(begin_time, anim.animation, anim.with_bars, anim.text, anim.text_color);
1400  anim.animation = nullptr;
1401  } else {
1402  anim.my_unit->anim_comp().get_animation()->update_parameters(anim.src, anim.src.get_direction(anim.my_unit->facing()));
1403  }
1404  }
1405 }
1406 
1408 {
1409  bool finished = true;
1410  for(const auto& anim : animated_units_) {
1411  finished &= anim.my_unit->anim_comp().get_animation()->animation_finished_potential();
1412  }
1413 
1414  return finished;
1415 }
1416 
1417 void unit_animator::wait_until(int animation_time) const
1418 {
1419  // important to set a max animation time so that the time does not go past this value for movements.
1420  // fix for bug #1565
1421  animated_units_[0].my_unit->anim_comp().get_animation()->set_max_animation_time(animation_time);
1422 
1423  display* disp = display::get_singleton();
1424  double speed = disp->turbo_speed();
1425 
1427 
1428  int end_tick = animated_units_[0].my_unit->anim_comp().get_animation()->time_to_tick(animation_time);
1429  while(SDL_GetTicks() < static_cast<unsigned int>(end_tick) - std::min<int>(static_cast<unsigned int>(20 / speed), 20)) {
1430  CVideo::delay(std::max<int>(0, std::min<int>(10, static_cast<int>((animation_time - get_animation_time()) * speed))));
1431 
1433  end_tick = animated_units_[0].my_unit->anim_comp().get_animation()->time_to_tick(animation_time);
1434  }
1435 
1436  CVideo::delay(std::max<int>(0, end_tick - SDL_GetTicks() + 5));
1437 
1439  animated_units_[0].my_unit->anim_comp().get_animation()->set_max_animation_time(0);
1440 }
1441 
1443 {
1444  if(game_config::no_delay) return;
1445 
1446  bool finished = false;
1447  while(!finished) {
1449 
1450  CVideo::delay(10);
1451 
1452  finished = true;
1453  for(const auto& anim : animated_units_) {
1454  finished &= anim.my_unit->anim_comp().get_animation()->animation_finished_potential();
1455  }
1456  }
1457 }
1458 
1460 {
1461  return animated_units_[0].my_unit->anim_comp().get_animation()->get_animation_time() ;
1462 }
1463 
1465 {
1466  return animated_units_[0].my_unit->anim_comp().get_animation()->get_animation_time_potential() ;
1467 }
1468 
1470 {
1471  int end_time = INT_MIN;
1472  for(const auto& anim : animated_units_) {
1473  if(anim.my_unit->anim_comp().get_animation()) {
1474  end_time = std::max<int>(end_time, anim.my_unit->anim_comp().get_animation()->get_end_time());
1475  }
1476  }
1477 
1478  return end_time;
1479 }
1480 
1482 {
1483  for(const auto& anim : animated_units_) {
1484  if(anim.my_unit->anim_comp().get_animation()) {
1485  anim.my_unit->anim_comp().get_animation()->pause_animation();
1486  }
1487  }
1488 }
1489 
1491 {
1492  for(const auto& anim : animated_units_) {
1493  if(anim.my_unit->anim_comp().get_animation()) {
1494  anim.my_unit->anim_comp().get_animation()->restart_animation();
1495  }
1496  }
1497 }
1498 
1500 {
1501  for(const auto& anim : animated_units_) {
1502  anim.my_unit->anim_comp().set_standing();
1503  }
1504 }
const frame_parameters parameters(int current_time) const
Getters for the different parameters.
Definition: frame.cpp:296
play_controller * controller
Definition: resources.cpp:21
boost::intrusive_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:30
void wait_for_end() const
Definition: animation.cpp:1442
const std::string & get_modifications() const
Definition: picture.hpp:90
bool empty() const
Tests for an attribute that either was never set or was set to "".
Describes a unit&#39;s animation sequence.
Definition: frame.hpp:200
Reserve layers to be selected for WML.
Definition: display.hpp:824
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:628
static DIRECTION parse_direction(const std::string &str)
Definition: location.cpp:64
All parameters from a frame at a given instant.
Definition: frame.hpp:34
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:88
unit_animation()=delete
std::vector< config::const_all_children_iterator > children
Definition: animation.cpp:54
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:923
static std::string get_heal_sound(const config &cfg)
Definition: animation.cpp:31
animation_cursor * parent
Definition: animation.cpp:175
void start_animations()
Definition: animation.cpp:1383
bool invalidate(const map_location &loc)
Function to invalidate a specific tile for redrawing.
Definition: display.cpp:2979
std::vector< hit_type > hits_
Definition: animation.hpp:183
This class represents a single unit of a specific type.
Definition: unit.hpp:99
Keep most parameters in a separate class to simplify the handling of the large number of parameters b...
Definition: frame.hpp:141
int get_current_frame_time() const
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
boost::iterator_range< const_all_children_iterator > const_all_children_itors
Definition: config.hpp:668
Variant for storing WML attributes.
std::vector< map_location::DIRECTION > directions_
Definition: animation.hpp:176
bool terrain_matches(const terrain_code &src, const terrain_code &dest)
Tests whether a specific terrain matches an expression, for matching rules see above.
frame_parsed_parameters parameters_
Definition: animation.hpp:167
bool would_end() const
Definition: animation.cpp:1407
animation_cursor(const config &cfg)
Definition: animation.cpp:61
void add_animation(const unit *animated_unit, const unit_animation *animation, const map_location &src=map_location::null_location(), bool with_bars=false, const std::string &text="", const color_t text_color={0, 0, 0})
Definition: animation.cpp:1325
std::vector< config > secondary_attack_filter_
Definition: animation.hpp:182
static void prepare_single_animation(const config &anim_cfg, animation_branches &expanded_anims)
Definition: animation.cpp:178
child_itors child_range(config_key_type key)
Definition: config.cpp:366
int get_current_frame_begin_time() const
Definition: animation.hpp:101
void add_frame(int duration, const unit_frame &value, bool force_change=false)
Definition: animation.hpp:53
void clear_haloes()
Definition: animation.cpp:1095
const unit_animation * animation
Definition: animation.hpp:269
std::vector< int > value_
Definition: animation.hpp:180
void add_frame(int duration, const unit_frame &value, bool force_change=false)
Adds a frame to an animation.
const unit_map & get_units() const
Definition: display.hpp:121
map_location::DIRECTION facing() const
The current directin this unit is facing within its hex.
Definition: unit.hpp:1272
virtual void play_slice(bool is_delay_enabled=true)
int get_begin_time() const
Definition: animation.cpp:1021
#define h
particle unit_anim_
Definition: animation.hpp:186
Audio output for sound and music.
Definition: sound.cpp:40
#define d
bool need_update() const
Definition: animation.cpp:957
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:326
bool need_update() const
Definition: animation.cpp:912
drawing_layer
The layers to render something on.
Definition: display.hpp:815
int get_end_time() const
Definition: animation.cpp:1011
std::vector< std::string > split(const std::string &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
unit_animation * get_animation() const
Get a pointer to the current animation.
bool need_minimal_update() const
Definition: animation.cpp:920
std::vector< std::string > debug_strings() const
Definition: frame.hpp:237
int get_current_frame_begin_time() const
map_location dst_
Definition: animation.hpp:189
int get_animation_time_potential() const
Definition: animation.cpp:1464
t_translation::terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition: map.cpp:309
void restart_animation()
Definition: animated.hpp:65
void pause_animation()
Definition: animation.cpp:1063
void wait_until(int animation_time) const
Definition: animation.cpp:1417
const T & get_frame(std::size_t n) const
particle(int start_time=0, const frame_builder &builder=frame_builder())
Definition: animation.hpp:132
void start_animation(int start_time)
Definition: animation.cpp:1286
void update_last_draw_time(double acceleration=0)
double turbo_speed() const
Definition: display.cpp:2357
void new_animation_frame()
Definition: animated.cpp:30
void restart_animation()
Definition: animation.cpp:1490
config & add_child_at(config_key_type key, const config &val, unsigned index)
Definition: config.cpp:511
const unit_animation * choose_animation(const display &disp, const map_location &loc, const std::string &event, const map_location &second_loc=map_location::null_location(), const int damage=0, const unit_animation::hit_type hit_type=unit_animation::hit_type::INVALID, const_attack_ptr attack=nullptr, const_attack_ptr second_attack=nullptr, int swing_num=0)
Chooses an appropriate animation from the list of known animations.
std::size_t get_frames_count() const
void replace_anim_if_invalid(const unit *animated_unit, const std::string &event, const map_location &src=map_location::null_location(), const map_location &dst=map_location::null_location(), const int value=0, bool with_bars=false, const std::string &text="", const color_t text_color={0, 0, 0}, const unit_animation::hit_type hit_type=unit_animation::hit_type::INVALID, const_attack_ptr attack=nullptr, const_attack_ptr second_attack=nullptr, int value2=0)
Definition: animation.cpp:1348
std::set< map_location > overlaped_hex_
Definition: animation.hpp:193
bool does_not_change() const
Definition: frame.hpp:227
unit_const_ptr my_unit
Definition: animation.hpp:268
int get_begin_time() const
std::vector< config > secondary_unit_filter_
Definition: animation.hpp:175
std::vector< std::string > event_
Definition: animation.hpp:179
map_display and display: classes which take care of displaying the map and game-data on the screen...
void redraw(const frame_parameters &value, const map_location &src, const map_location &dst, halo::manager &halo_man)
Definition: animation.cpp:1245
all_children_iterator erase(const all_children_iterator &i)
Definition: config.cpp:691
const unit_frame & get_last_frame() const
Definition: animation.hpp:48
void restart_animation()
Definition: animation.cpp:1072
void pause_animation()
Definition: animated.hpp:60
Encapsulates the map of the game.
Definition: location.hpp:42
boost::iterator_range< const_child_iterator > const_child_itors
Definition: config.hpp:238
const unit_frame & get_current_frame() const
unit_iterator find(std::size_t id)
Definition: map.cpp:311
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:481
bool animation_finished() const
Returns true if the current animation was finished.
std::size_t i
Definition: function.cpp:933
unit_animation_component & anim_comp() const
Definition: unit.hpp:1443
animation_branches branches
Definition: animation.cpp:174
int get_end_time() const
Definition: animation.cpp:1469
static void add_simple_anim(std::vector< unit_animation > &animations, const config &cfg, char const *tag_name, char const *apply_to, display::drawing_layer layer=display::LAYER_UNIT_DEFAULT, bool offscreen=true)
Definition: animation.cpp:606
default layer for drawing moving units
Definition: display.hpp:835
friend std::ostream & operator<<(std::ostream &outstream, const unit_animation &u_animation)
Definition: animation.cpp:1155
std::list< animation_branch > animation_branches
Definition: animation.cpp:57
mock_party p
bool animation_finished_potential() const
default layer for drawing units
Definition: display.hpp:826
void pause_animation()
Definition: animation.cpp:1481
map_location src_
Definition: animation.hpp:188
default layer for missile frames
Definition: display.hpp:840
bool tile_nearly_on_screen(const map_location &loc) const
Checks if location loc or one of the adjacent tiles is visible on screen.
Definition: display.cpp:2071
std::vector< int > value2_
Definition: animation.hpp:184
config::const_all_children_itors itors
Definition: animation.cpp:172
DIRECTION
Valid directions which can be moved in our hexagonal world.
Definition: location.hpp:44
config merge() const
Definition: animation.cpp:43
const std::string & get_filename() const
Definition: picture.hpp:85
std::vector< config > unit_filter_
Definition: animation.hpp:174
config & add_child(config_key_type key)
Definition: config.cpp:479
int get_animation_time() const
void start_animation(int start_time, const map_location &src=map_location::null_location(), const map_location &dst=map_location::null_location(), const std::string &text="", const color_t text_color={0, 0, 0}, const bool accelerate=true)
Definition: animation.cpp:1031
void override(int start_time, int duration, const cycle_state cycles, 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: animation.cpp:886
std::vector< config > primary_attack_filter_
Definition: animation.hpp:181
int get_animation_time() const
Definition: animation.cpp:1459
int duration() const
Definition: frame.hpp:222
int matches(const display &disp, const map_location &loc, const map_location &second_loc, const unit *my_unit, const std::string &event="", const int value=0, hit_type hit=hit_type::INVALID, const_attack_ptr attack=nullptr, const_attack_ptr second_attack=nullptr, int value2=0) const
Definition: animation.cpp:377
const gamemap & get_map() const
Definition: display.hpp:92
void update_parameters(const map_location &src, const map_location &dst)
Definition: animation.cpp:1057
#define f
std::map< std::string, particle > sub_anims_
Definition: animation.hpp:185
bool find(E event, F functor)
Tests whether an event handler is available.
std::string debug() const
Definition: animation.cpp:1148
void set_all_standing()
Definition: animation.cpp:1499
int get_current_frame_end_time() const
A variable-expanding proxy for the config class.
Definition: variable.hpp:42
ter_list read_list(utils::string_view str, const ter_layer filler)
Reads a list of terrains from a string, when reading the.
this module manages the cache of images.
static animation_branches prepare_animation(const config &cfg, const std::string &animation_tag)
Definition: animation.cpp:243
static void delay(unsigned int milliseconds)
Waits a given number of milliseconds before returning.
Definition: video.cpp:330
void redraw(frame_parameters &value, halo::manager &halo_man)
Definition: animation.cpp:1081
void update_last_draw_time()
Definition: animation.cpp:1002
bool need_minimal_update() const
Definition: animation.cpp:967
bool animation_finished_potential() const
Definition: animation.cpp:992
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:92
bool valid() const
Definition: map.hpp:276
mock_char c
std::shared_ptr< const attack_type > const_attack_ptr
Definition: ptr.hpp:37
bool play_offscreen_
Definition: animation.hpp:192
t_translation::ter_list terrain_types_
Definition: animation.hpp:173
static std::string write_direction(DIRECTION dir)
Definition: location.cpp:139
animation_cursor(const config &cfg, animation_cursor *p)
Definition: animation.cpp:67
boost::tribool primary_frame
Definition: frame.hpp:68
static rng & default_instance()
Definition: random.cpp:73
bool does_not_change() const
Definition: frame.cpp:272
bool empty() const
Definition: config.cpp:886
std::string debug() const
Definition: config.cpp:1279
int get_end_time() const
std::set< map_location > get_overlaped_hex(const frame_parameters &value, const map_location &src, const map_location &dst)
Definition: animation.cpp:1274
bool propagate_invalidation(const std::set< map_location > &locs)
If this set is partially invalidated, invalidate all its hexes.
Definition: display.cpp:3006
bool invalidate(frame_parameters &value)
Definition: animation.cpp:1104
bool animation_finished() const
Definition: animation.cpp:982
void start_animation(int start_time, bool cycles=false)
Starts an animation cycle.
int get_animation_duration() const