The Battle for Wesnoth  1.13.11+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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 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/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.push_back(animation_cursor(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.push_back(animation_cursor(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"]))
280  , unit_filter_()
281  , secondary_unit_filter_()
282  , directions_()
283  , frequency_(cfg["frequency"])
284  , base_score_(cfg["base_score"])
285  , event_()
286  , value_()
287  , primary_attack_filter_()
288  , secondary_attack_filter_()
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_.push_back(config());
563  animations.back().primary_attack_filter_.back()["range"] = "melee";
564 
565  animations.push_back(base);
566  animations.back().event_ = { "attack" };
567  animations.back().unit_anim_.override(-150, 150, particle::NO_CYCLE);
568  animations.back().primary_attack_filter_.push_back(config());
569  animations.back().primary_attack_filter_.back()["range"] = "ranged";
570 
571  animations.push_back(base);
572  animations.back().event_ = { "death" };
573  animations.back().unit_anim_.override(0, 600, particle::NO_CYCLE, "1~0:600");
574  animations.back().sub_anims_["_death_sound"] = particle();
575  animations.back().sub_anims_["_death_sound"].add_frame(1, frame_builder().sound(cfg["die_sound"]), true);
576 
577  animations.push_back(base);
578  animations.back().event_ = { "victory" };
579  animations.back().unit_anim_.override(0, animations.back().unit_anim_.get_animation_duration(), particle::CYCLE);
580 
581  animations.push_back(base);
582  animations.back().unit_anim_.override(0, 150, particle::NO_CYCLE, "1~0:150");
583  animations.back().event_ = { "pre_teleport" };
584 
585  animations.push_back(base);
586  animations.back().unit_anim_.override(0, 150, particle::NO_CYCLE, "0~1:150,1");
587  animations.back().event_ = { "post_teleport" };
588 
589  animations.push_back(base);
590  animations.back().event_ = { "healing" };
591 
592  animations.push_back(base);
593  animations.back().event_ = { "healed" };
594  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});
595 
596  const std::string healed_sound = get_heal_sound(cfg);
597 
598  animations.back().sub_anims_["_healed_sound"].add_frame(1, frame_builder().sound(healed_sound), true);
599 
600  animations.push_back(base);
601  animations.back().event_ = { "poisoned" };
602  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});
603  animations.back().sub_anims_["_poison_sound"] = particle();
604  animations.back().sub_anims_["_poison_sound"].add_frame(1, frame_builder().sound(game_config::sounds::status::poisoned), true);
605  }
606 }
607 
608 static void add_simple_anim(std::vector<unit_animation>& animations,
609  const config& cfg, char const* tag_name, char const* apply_to,
611  bool offscreen = true)
612 {
613  for(const animation_branch& ab : prepare_animation(cfg, tag_name)) {
614  config anim = ab.merge();
615  anim["apply_to"] = apply_to;
616 
617  if(!offscreen) {
618  config::attribute_value& v = anim["offscreen"];
619  if(v.empty()) v = false;
620  }
621 
622  config::attribute_value& v = anim["layer"];
623  if(v.empty()) v = layer - display::LAYER_UNIT_FIRST;
624 
625  animations.push_back(unit_animation(anim));
626  }
627 }
628 
629 void unit_animation::add_anims( std::vector<unit_animation> & animations, const config & cfg)
630 {
631  for(const animation_branch& ab : prepare_animation(cfg, "animation")) {
632  animations.push_back(unit_animation(ab.merge()));
633  }
634 
635  const int default_layer = display::LAYER_UNIT_DEFAULT - display::LAYER_UNIT_FIRST;
638 
639  add_simple_anim(animations, cfg, "resistance_anim", "resistance");
640  add_simple_anim(animations, cfg, "leading_anim", "leading");
641  add_simple_anim(animations, cfg, "recruit_anim", "recruited");
642  add_simple_anim(animations, cfg, "recruiting_anim", "recruiting");
643  add_simple_anim(animations, cfg, "idle_anim", "idling", display::LAYER_UNIT_DEFAULT, false);
644  add_simple_anim(animations, cfg, "levelin_anim", "levelin");
645  add_simple_anim(animations, cfg, "levelout_anim", "levelout");
646 
647  for(const animation_branch& ab : prepare_animation(cfg, "standing_anim")) {
648  config anim = ab.merge();
649  anim["apply_to"] = "standing";
650  anim["cycles"] = true;
651 
652  // Add cycles to all frames within a standing animation block
653  for(config::const_all_children_iterator ci : ab.children) {
654  std::string sub_frame_name = ci->key;
655  size_t pos = sub_frame_name.find("_frame");
656  if(pos != std::string::npos) {
657  anim[sub_frame_name.substr(0, pos) + "_cycles"] = true;
658  }
659  }
660 
661  if(anim["layer"].empty()) {
662  anim["layer"] = default_layer;
663  }
664 
665  if(anim["offscreen"].empty()) {
666  anim["offscreen"] = false;
667  }
668 
669  animations.push_back(unit_animation(anim));
670  }
671 
672  // Standing animations are also used as default animations
673  for(const animation_branch& ab : prepare_animation(cfg, "standing_anim")) {
674  config anim = ab.merge();
675  anim["apply_to"] = "default";
676  anim["cycles"] = true;
677 
678  for(config::const_all_children_iterator ci : ab.children) {
679  std::string sub_frame_name = ci->key;
680  size_t pos = sub_frame_name.find("_frame");
681  if(pos != std::string::npos) {
682  anim[sub_frame_name.substr(0, pos) + "_cycles"] = true;
683  }
684  }
685 
686  if(anim["layer"].empty()) {
687  anim["layer"] = default_layer;
688  }
689 
690  if(anim["offscreen"].empty()) {
691  anim["offscreen"] = false;
692  }
693 
694  animations.push_back(unit_animation(anim));
695  }
696 
697  for(const animation_branch& ab : prepare_animation(cfg, "healing_anim")) {
698  config anim = ab.merge();
699  anim["apply_to"] = "healing";
700  anim["value"] = anim["damage"];
701 
702  if(anim["layer"].empty()) {
703  anim["layer"] = default_layer;
704  }
705 
706  animations.push_back(unit_animation(anim));
707  }
708 
709  for(const animation_branch& ab : prepare_animation(cfg, "healed_anim")) {
710  config anim = ab.merge();
711  anim["apply_to"] = "healed";
712  anim["value"] = anim["healing"];
713 
714  if(anim["layer"].empty()) {
715  anim["layer"] = default_layer;
716  }
717 
718  animations.push_back(unit_animation(anim));
719  animations.back().sub_anims_["_healed_sound"] = particle();
720 
721  const std::string healed_sound = get_heal_sound(cfg);
722  animations.back().sub_anims_["_healed_sound"].add_frame(1,frame_builder().sound(healed_sound),true);
723  }
724 
725  for(const animation_branch &ab : prepare_animation(cfg, "poison_anim")) {
726  config anim = ab.merge();
727  anim["apply_to"] = "poisoned";
728  anim["value"] = anim["damage"];
729 
730  if(anim["layer"].empty()) {
731  anim["layer"] = default_layer;
732  }
733 
734  animations.push_back(unit_animation(anim));
735  animations.back().sub_anims_["_poison_sound"] = particle();
736  animations.back().sub_anims_["_poison_sound"].add_frame(1,frame_builder().sound(game_config::sounds::status::poisoned),true);
737  }
738 
739  add_simple_anim(animations, cfg, "pre_movement_anim", "pre_movement", display::LAYER_UNIT_MOVE_DEFAULT);
740 
741  for(const animation_branch& ab : prepare_animation(cfg, "movement_anim")) {
742  config anim = ab.merge();
743  anim["apply_to"] = "movement";
744 
745  if(anim["offset"].empty()) {
746  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,";
747  }
748 
749  if(anim["layer"].empty()) {
750  anim["layer"] = move_layer;
751  }
752 
753  animations.push_back(unit_animation(anim));
754  }
755 
756  add_simple_anim(animations, cfg, "post_movement_anim", "post_movement", display::LAYER_UNIT_MOVE_DEFAULT);
757 
758  for(const animation_branch& ab : prepare_animation(cfg, "defend")) {
759  config anim = ab.merge();
760  anim["apply_to"] = "defend";
761 
762  if(anim["layer"].empty()) {
763  anim["layer"] = default_layer;
764  }
765 
766  if(!anim["damage"].empty() && anim["value"].empty()) {
767  anim["value"] = anim["damage"];
768  }
769 
770  if(anim["hits"].empty()) {
771  anim["hits"] = false;
772  animations.push_back(unit_animation(anim));
773  animations.back().base_score_--; //so default doesn't interefere with 'if' block
774 
775  anim["hits"] = true;
776  animations.push_back(unit_animation(anim));
777  animations.back().base_score_--;
778 
779  image::locator image_loc = animations.back().get_last_frame().end_parameters().image;
780  animations.back().add_frame(225, frame_builder()
781  .image(image_loc.get_filename()+image_loc.get_modifications())
782  .duration(225)
783  .blend("0.0,0.5:75,0.0:75,0.5:75,0.0", {255,0,0}));
784  } else {
785  for(const std::string& hit_type : utils::split(anim["hits"])) {
786  config tmp = anim;
787  tmp["hits"] = hit_type;
788 
789  animations.push_back(unit_animation(tmp));
790 
791  image::locator image_loc = animations.back().get_last_frame().end_parameters().image;
792  if(hit_type == "yes" || hit_type == "hit" || hit_type=="kill") {
793  animations.back().add_frame(225, frame_builder()
794  .image(image_loc.get_filename() + image_loc.get_modifications())
795  .duration(225)
796  .blend("0.0,0.5:75,0.0:75,0.5:75,0.0", {255,0,0}));
797  }
798  }
799  }
800  }
801 
802  add_simple_anim(animations, cfg, "draw_weapon_anim", "draw_weapon", display::LAYER_UNIT_MOVE_DEFAULT);
803  add_simple_anim(animations, cfg, "sheath_weapon_anim", "sheath_weapon", display::LAYER_UNIT_MOVE_DEFAULT);
804 
805  for(const animation_branch& ab : prepare_animation(cfg, "attack_anim")) {
806  config anim = ab.merge();
807  anim["apply_to"] = "attack";
808 
809  if(anim["layer"].empty()) {
810  anim["layer"] = move_layer;
811  }
812 
813  config::const_child_itors missile_fs = anim.child_range("missile_frame");
814  if(anim["offset"].empty() && missile_fs.empty()) {
815  anim["offset"] ="0~0.6,0.6~0";
816  }
817 
818  if(!missile_fs.empty()) {
819  if(anim["missile_offset"].empty()) {
820  anim["missile_offset"] = "0~0.8";
821  }
822 
823  if(anim["missile_layer"].empty()) {
824  anim["missile_layer"] = missile_layer;
825  }
826 
827  config tmp;
828  tmp["duration"] = 1;
829 
830  anim.add_child("missile_frame", tmp);
831  anim.add_child_at("missile_frame", tmp, 0);
832  }
833 
834  animations.push_back(unit_animation(anim));
835  }
836 
837  for(const animation_branch& ab : prepare_animation(cfg, "death")) {
838  config anim = ab.merge();
839  anim["apply_to"] = "death";
840 
841  if(anim["layer"].empty()) {
842  anim["layer"] = default_layer;
843  }
844 
845  animations.push_back(unit_animation(anim));
846  image::locator image_loc = animations.back().get_last_frame().end_parameters().image;
847 
848  animations.back().add_frame(600, frame_builder()
849  .image(image_loc.get_filename()+image_loc.get_modifications())
850  .duration(600)
851  .highlight("1~0:600"));
852 
853  if(!cfg["die_sound"].empty()) {
854  animations.back().sub_anims_["_death_sound"] = particle();
855  animations.back().sub_anims_["_death_sound"].add_frame(1,frame_builder().sound(cfg["die_sound"]),true);
856  }
857  }
858 
859  add_simple_anim(animations, cfg, "victory_anim", "victory");
860 
861  for(const animation_branch& ab : prepare_animation(cfg, "extra_anim")) {
862  config anim = ab.merge();
863  anim["apply_to"] = anim["flag"];
864 
865  if(anim["layer"].empty()) {
866  anim["layer"] = default_layer;
867  }
868 
869  animations.push_back(unit_animation(anim));
870  }
871 
872  for(const animation_branch& ab : prepare_animation(cfg, "teleport_anim")) {
873  config anim = ab.merge();
874  if(anim["layer"].empty()) {
875  anim["layer"] = default_layer;
876  }
877 
878  anim["apply_to"] = "pre_teleport";
879  animations.push_back(unit_animation(anim));
880  animations.back().unit_anim_.set_end_time(0);
881 
882  anim["apply_to"] ="post_teleport";
883  animations.push_back(unit_animation(anim));
884  animations.back().unit_anim_.remove_frames_until(0);
885  }
886 }
887 
889  , int duration
890  , const cycle_state cycles
891  , const std::string& highlight
892  , const std::string& blend_ratio
893  , color_t blend_color
894  , const std::string& offset
895  , const std::string& layer
896  , const std::string& modifiers)
897 {
898  set_begin_time(start_time);
899  parameters_.override(duration,highlight,blend_ratio,blend_color,offset,layer,modifiers);
900 
901  if(cycles == CYCLE) {
902  cycles_=true;
903  } else if(cycles==NO_CYCLE) {
904  cycles_=false;
905  }
906 
907  if(get_animation_duration() < duration) {
909  } else if(get_animation_duration() > duration) {
910  set_end_time(duration);
911  }
912 }
913 
915 {
916  if(animated<unit_frame>::need_update()) return true;
917  if(get_current_frame().need_update()) return true;
918  if(parameters_.need_update()) return true;
919  return false;
920 }
921 
923 {
924  return get_current_frame_begin_time() != last_frame_begin_time_;
925 }
926 
927 unit_animation::particle::particle(const config& cfg, const std::string& frame_string)
928  : animated<unit_frame>()
929  , accelerate(true)
930  , parameters_()
931  , halo_id_()
932  , last_frame_begin_time_(0)
933  , cycles_(false)
934 {
935  starting_frame_time_ = INT_MAX;
936 
937  config::const_child_itors range = cfg.child_range(frame_string + "frame");
938  if(!range.empty() && cfg[frame_string + "start_time"].empty()) {
939  for(const config& frame : range) {
940  starting_frame_time_ = std::min(starting_frame_time_, frame["begin"].to_int());
941  }
942  } else {
943  starting_frame_time_ = cfg[frame_string + "start_time"];
944  }
945 
946  for(const config& frame : range) {
947  unit_frame tmp_frame(frame);
948  add_frame(tmp_frame.duration(), tmp_frame, !tmp_frame.does_not_change());
949  }
950 
951  cycles_ = cfg[frame_string + "cycles"].to_bool(false);
953 
955  force_change();
956  }
957 }
958 
960 {
961  if(unit_anim_.need_update()) return true;
962  for(const auto& anim : sub_anims_) {
963  if(anim.second.need_update()) return true;
964  }
965 
966  return false;
967 }
968 
970 {
971  if(!play_offscreen_) {
972  return false;
973  }
974 
975  if(unit_anim_.need_minimal_update()) return true;
976 
977  for(const auto& anim : sub_anims_) {
978  if(anim.second.need_minimal_update()) return true;
979  }
980 
981  return false;
982 }
983 
985 {
986  if(!unit_anim_.animation_finished()) return false;
987  for(const auto& anim : sub_anims_) {
988  if(!anim.second.animation_finished()) return false;
989  }
990 
991  return true;
992 }
993 
995 {
996  if(!unit_anim_.animation_finished_potential()) return false;
997  for(const auto& anim : sub_anims_) {
998  if(!anim.second.animation_finished_potential()) return false;
999  }
1000 
1001  return true;
1002 }
1003 
1005 {
1006  double acceleration = unit_anim_.accelerate ? display::get_singleton()->turbo_speed() : 1.0;
1007  unit_anim_.update_last_draw_time(acceleration);
1008  for(auto& anim : sub_anims_) {
1009  anim.second.update_last_draw_time(acceleration);
1010  }
1011 }
1012 
1014 {
1015  int result = unit_anim_.get_end_time();
1016  for(const auto& anim : sub_anims_) {
1017  result = std::max<int>(result, anim.second.get_end_time());
1018  }
1019 
1020  return result;
1021 }
1022 
1024 {
1025  int result = unit_anim_.get_begin_time();
1026  for(const auto& anim : sub_anims_) {
1027  result = std::min<int>(result, anim.second.get_begin_time());
1028  }
1029 
1030  return result;
1031 }
1032 
1034  , const map_location& src
1035  , const map_location& dst
1036  , const std::string& text
1037  , const color_t text_color
1038  , const bool accelerate)
1039 {
1040  unit_anim_.accelerate = accelerate;
1041  src_ = src;
1042  dst_ = dst;
1043 
1044  unit_anim_.start_animation(start_time);
1045 
1046  if(!text.empty()) {
1047  particle crude_build;
1048  crude_build.add_frame(1, frame_builder());
1049  crude_build.add_frame(1, frame_builder().text(text, text_color), true);
1050  sub_anims_["_add_text"] = crude_build;
1051  }
1052 
1053  for(auto& anim : sub_anims_) {
1054  anim.second.accelerate = accelerate;
1055  anim.second.start_animation(start_time);
1056  }
1057 }
1058 
1060 {
1061  src_ = src;
1062  dst_ = dst;
1063 }
1064 
1066 {
1068 
1069  for(auto& anim : sub_anims_) {
1070  anim.second.pause_animation();
1071  }
1072 }
1073 
1075 {
1077 
1078  for(auto& anim : sub_anims_) {
1079  anim.second.restart_animation();
1080  }
1081 }
1082 
1084 {
1085  invalidated_ = false;
1086  overlaped_hex_.clear();
1087 
1088  value.primary_frame = true;
1089  unit_anim_.redraw(value,src_,dst_, halo_man);
1090 
1091  value.primary_frame = false;
1092  for(auto& anim : sub_anims_) {
1093  anim.second.redraw(value, src_, dst_, halo_man);
1094  }
1095 }
1096 
1098 {
1100 
1101  for(auto& anim : sub_anims_) {
1102  anim.second.clear_halo();
1103  }
1104 }
1105 
1107 {
1108  if(invalidated_) return false;
1109 
1110  display* disp = display::get_singleton();
1111  const bool complete_redraw = disp->tile_nearly_on_screen(src_) || disp->tile_nearly_on_screen(dst_);
1112 
1113  if(overlaped_hex_.empty()) {
1114  if(complete_redraw) {
1115  value.primary_frame = true;
1117  value.primary_frame = false;
1118 
1119  for(auto& anim : sub_anims_) {
1120  std::set<map_location> tmp = anim.second.get_overlaped_hex(value, src_, dst_);
1121  overlaped_hex_.insert(tmp.begin(), tmp.end());
1122  }
1123  } else {
1124  // Offscreen animations only invalidate their own hex, no propagation,
1125  // but we still need this to play sounds
1126  overlaped_hex_.insert(src_);
1127  }
1128  }
1129 
1130  if(complete_redraw) {
1131  if( need_update()) {
1132  disp->invalidate(overlaped_hex_);
1133  invalidated_ = true;
1134  return true;
1135  } else {
1137  return invalidated_;
1138  }
1139  } else {
1140  if(need_minimal_update()) {
1141  disp->invalidate(overlaped_hex_);
1142  invalidated_ = true;
1143  return true;
1144  } else {
1145  return false;
1146  }
1147  }
1148 }
1149 
1151 {
1152  std::ostringstream outstream;
1153  outstream << *this;
1154  return outstream.str();
1155 }
1156 
1157 std::ostream& operator<<(std::ostream& outstream, const unit_animation& u_animation)
1158 {
1159  std::string events_string = utils::join(u_animation.event_);
1160  outstream << "[" << events_string << "]\n";
1161 
1162  outstream << "\tstart_time=" << u_animation.get_begin_time() << '\n';
1163 
1164  if(u_animation.hits_.size() > 0) {
1165  std::vector<std::string> hits;
1166  std::transform(u_animation.hits_.begin(), u_animation.hits_.end(), std::back_inserter(hits), unit_animation::hit_type::enum_to_string);
1167  outstream << "\thits=" << utils::join(hits) << '\n';
1168  }
1169 
1170  if(u_animation.directions_.size() > 0) {
1171  std::vector<std::string> dirs;
1172  std::transform(u_animation.directions_.begin(), u_animation.directions_.end(), std::back_inserter(dirs), map_location::write_direction);
1173  outstream << "\tdirections=" << utils::join(dirs) << '\n';
1174  }
1175 
1176  if(u_animation.terrain_types_.size() > 0) {
1177  outstream << "\tterrain=" << utils::join(u_animation.terrain_types_) << '\n';
1178  }
1179 
1180  if(u_animation.frequency_ > 0) outstream << "frequency=" << u_animation.frequency_ << '\n';
1181 
1182  if(u_animation.unit_filter_.size() > 0) {
1183  outstream << "[filter]\n";
1184  for(const config& cfg : u_animation.unit_filter_) {
1185  outstream << cfg.debug();
1186  }
1187 
1188  outstream << "[/filter]\n";
1189  }
1190 
1191  if(u_animation.secondary_unit_filter_.size() > 0) {
1192  outstream << "[filter_second]\n";
1193  for(const config& cfg : u_animation.secondary_unit_filter_) {
1194  outstream << cfg.debug();
1195  }
1196 
1197  outstream << "[/filter_second]\n";
1198  }
1199 
1200  if(u_animation.primary_attack_filter_.size() > 0) {
1201  outstream << "[filter_attack]\n";
1202  for(const config cfg : u_animation.primary_attack_filter_) {
1203  outstream << cfg.debug();
1204  }
1205 
1206  outstream << "[/filter_attack]\n";
1207  }
1208 
1209  if(u_animation.secondary_attack_filter_.size() > 0) {
1210  outstream << "[filter_second_attack]\n";
1211  for(const config cfg : u_animation.secondary_attack_filter_) {
1212  outstream << cfg.debug();
1213  }
1214 
1215  outstream << "[/filter_second_attack]\n";
1216  }
1217 
1218  for(size_t i = 0; i < u_animation.unit_anim_.get_frames_count(); i++) {
1219  outstream << "\t[frame]\n";
1220  for(const std::string frame_string : u_animation.unit_anim_.get_frame(i).debug_strings()) {
1221  outstream << "\t\t" << frame_string <<"\n";
1222  }
1223  outstream << "\t[/frame]\n";
1224  }
1225 
1226  for(std::pair<std::string, unit_animation::particle> p : u_animation.sub_anims_) {
1227  for(size_t i = 0; i < p.second.get_frames_count(); i++) {
1228  std::string sub_frame_name = p.first;
1229  size_t pos = sub_frame_name.find("_frame");
1230  if(pos != std::string::npos) sub_frame_name = sub_frame_name.substr(0, pos);
1231 
1232  outstream << "\t" << sub_frame_name << "_start_time=" << p.second.get_begin_time() << '\n';
1233  outstream << "\t[" << p.first << "]\n";
1234 
1235  for(const std::string frame_string : p.second.get_frame(i).debug_strings()) {
1236  outstream << "\t\t" << frame_string << '\n';
1237  }
1238 
1239  outstream << "\t[/" << p.first << "]\n";
1240  }
1241  }
1242 
1243  outstream << "[/" << events_string << "]\n";
1244  return outstream;
1245 }
1246 
1248 {
1249  const unit_frame& current_frame = get_current_frame();
1250  const int animation_time = get_animation_time();
1251  const frame_parameters default_val = parameters_.parameters(animation_time - get_begin_time());
1252 
1253  // Everything is relative to the first frame in an attack/defense/etc. block.
1254  // so we need to check if this particular frame is due to be shown at this time
1255  bool in_scope_of_frame = (animation_time >= get_current_frame_begin_time() ? true: false);
1256  if(animation_time > get_current_frame_end_time()) in_scope_of_frame = false;
1257 
1258  // Sometimes even if the frame is not due to be shown, a frame image still must be shown.
1259  // i.e. in a defense animation that is shorter than an attack animation.
1260  // the halos should not persist though and use the 'in_scope_of_frame' variable.
1261 
1262  // For sound frames we want the first time variable set only after the frame has started.
1263  if(get_current_frame_begin_time() != last_frame_begin_time_ && animation_time >= get_current_frame_begin_time()) {
1264  last_frame_begin_time_ = get_current_frame_begin_time();
1265  current_frame.redraw(get_current_frame_time(), true, in_scope_of_frame, src, dst, halo_id_, halo_man, default_val, value);
1266  } else {
1267  current_frame.redraw(get_current_frame_time(), false, in_scope_of_frame, src, dst, halo_id_, halo_man, default_val, value);
1268  }
1269 }
1270 
1272 {
1273  halo_id_.reset();
1274 }
1275 
1276 std::set<map_location> unit_animation::particle::get_overlaped_hex(const frame_parameters& value, const map_location& src, const map_location& dst)
1277 {
1278  const unit_frame& current_frame = get_current_frame();
1279  const frame_parameters default_val = parameters_.parameters(get_animation_time() - get_begin_time());
1280  return current_frame.get_overlaped_hex(get_current_frame_time(), src, dst, default_val,value);
1281 }
1282 
1284 {
1285  halo_id_.reset();
1286 }
1287 
1289 {
1290  halo_id_.reset();
1291  parameters_.override(get_animation_duration());
1292  animated<unit_frame>::start_animation(start_time,cycles_);
1293  last_frame_begin_time_ = get_begin_time() -1;
1294 }
1295 
1296 void unit_animator::add_animation(const unit* animated_unit
1297  , const std::string& event
1298  , const map_location &src
1299  , const map_location &dst
1300  , const int value
1301  , bool with_bars
1302  , const std::string& text
1303  , const color_t text_color
1304  , const unit_animation::hit_type hit_type
1305  , const_attack_ptr attack
1306  , const_attack_ptr second_attack
1307  , int value2)
1308 {
1309  if(!animated_unit) return;
1310 
1311  display* disp = display::get_singleton();
1312 
1313  anim_elem tmp;
1314  tmp.my_unit = unit_const_ptr(animated_unit);
1315  tmp.text = text;
1316  tmp.text_color = text_color;
1317  tmp.src = src;
1318  tmp.with_bars= with_bars;
1319  tmp.animation = animated_unit->anim_comp().choose_animation(*disp, src, event, dst, value, hit_type, attack, second_attack, value2);
1320 
1321  if(!tmp.animation) return;
1322 
1323  start_time_ = std::max<int>(start_time_, tmp.animation->get_begin_time());
1324  animated_units_.push_back(std::move(tmp));
1325 }
1326 
1327 void unit_animator::add_animation(const unit* animated_unit
1328  , const unit_animation* anim
1329  , const map_location &src
1330  , bool with_bars
1331  , const std::string& text
1332  , const color_t text_color)
1333 {
1334  if(!animated_unit) return;
1335 
1336  anim_elem tmp;
1337  tmp.my_unit = unit_const_ptr(animated_unit);
1338  tmp.text = text;
1339  tmp.text_color = text_color;
1340  tmp.src = src;
1341  tmp.with_bars = with_bars;
1342  tmp.animation = anim;
1343 
1344  if(!tmp.animation) return;
1345 
1346  start_time_ = std::max<int>(start_time_, tmp.animation->get_begin_time());
1347  animated_units_.push_back(std::move(tmp));
1348 }
1349 
1351  , const std::string& event
1352  , const map_location &src
1353  , const map_location & dst
1354  , const int value
1355  , bool with_bars
1356  , const std::string& text
1357  , const color_t text_color
1358  , const unit_animation::hit_type hit_type
1359  , const_attack_ptr attack
1360  , const_attack_ptr second_attack
1361  , int value2)
1362 {
1363  if(!animated_unit) return;
1364 
1365  display* disp = display::get_singleton();
1366  if(animated_unit->anim_comp().get_animation() &&
1367  !animated_unit->anim_comp().get_animation()->animation_finished_potential() &&
1368  animated_unit->anim_comp().get_animation()->matches(
1369  *disp, src, dst, animated_unit, event, value, hit_type, attack, second_attack, value2) > unit_animation::MATCH_FAIL)
1370  {
1371  anim_elem tmp;
1372  tmp.my_unit = unit_const_ptr(animated_unit);
1373  tmp.text = text;
1374  tmp.text_color = text_color;
1375  tmp.src = src;
1376  tmp.with_bars= with_bars;
1377  tmp.animation = nullptr;
1378 
1379  animated_units_.push_back(tmp);
1380  } else {
1381  add_animation(animated_unit,event,src,dst,value,with_bars,text,text_color,hit_type,attack,second_attack,value2);
1382  }
1383 }
1384 
1386 {
1387  int begin_time = INT_MAX;
1388 
1389  for(const auto& anim : animated_units_) {
1390  if(anim.my_unit->anim_comp().get_animation()) {
1391  if(anim.animation) {
1392  begin_time = std::min<int>(begin_time, anim.animation->get_begin_time());
1393  } else {
1394  begin_time = std::min<int>(begin_time, anim.my_unit->anim_comp().get_animation()->get_begin_time());
1395  }
1396  }
1397  }
1398 
1399  for(auto& anim : animated_units_) {
1400  if(anim.animation) {
1401  anim.my_unit->anim_comp().start_animation(begin_time, anim.animation, anim.with_bars, anim.text, anim.text_color);
1402  anim.animation = nullptr;
1403  } else {
1404  anim.my_unit->anim_comp().get_animation()->update_parameters(anim.src, anim.src.get_direction(anim.my_unit->facing()));
1405  }
1406  }
1407 }
1408 
1410 {
1411  bool finished = true;
1412  for(const auto& anim : animated_units_) {
1413  finished &= anim.my_unit->anim_comp().get_animation()->animation_finished_potential();
1414  }
1415 
1416  return finished;
1417 }
1418 
1419 void unit_animator::wait_until(int animation_time) const
1420 {
1421  // important to set a max animation time so that the time does not go past this value for movements.
1422  // fix for bug #1565
1423  animated_units_[0].my_unit->anim_comp().get_animation()->set_max_animation_time(animation_time);
1424 
1425  display* disp = display::get_singleton();
1426  double speed = disp->turbo_speed();
1427 
1429 
1430  int end_tick = animated_units_[0].my_unit->anim_comp().get_animation()->time_to_tick(animation_time);
1431  while(SDL_GetTicks() < static_cast<unsigned int>(end_tick) - std::min<int>(static_cast<unsigned int>(20 / speed), 20)) {
1432  CVideo::delay(std::max<int>(0, std::min<int>(10, static_cast<int>((animation_time - get_animation_time()) * speed))));
1433 
1435  end_tick = animated_units_[0].my_unit->anim_comp().get_animation()->time_to_tick(animation_time);
1436  }
1437 
1438  CVideo::delay(std::max<int>(0, end_tick - SDL_GetTicks() + 5));
1439 
1441  animated_units_[0].my_unit->anim_comp().get_animation()->set_max_animation_time(0);
1442 }
1443 
1445 {
1446  if(game_config::no_delay) return;
1447 
1448  bool finished = false;
1449  while(!finished) {
1451 
1452  CVideo::delay(10);
1453 
1454  finished = true;
1455  for(const auto& anim : animated_units_) {
1456  finished &= anim.my_unit->anim_comp().get_animation()->animation_finished_potential();
1457  }
1458  }
1459 }
1460 
1462 {
1463  return animated_units_[0].my_unit->anim_comp().get_animation()->get_animation_time() ;
1464 }
1465 
1467 {
1468  return animated_units_[0].my_unit->anim_comp().get_animation()->get_animation_time_potential() ;
1469 }
1470 
1472 {
1473  int end_time = INT_MIN;
1474  for(const auto& anim : animated_units_) {
1475  if(anim.my_unit->anim_comp().get_animation()) {
1476  end_time = std::max<int>(end_time, anim.my_unit->anim_comp().get_animation()->get_end_time());
1477  }
1478  }
1479 
1480  return end_time;
1481 }
1482 
1484 {
1485  for(const auto& anim : animated_units_) {
1486  if(anim.my_unit->anim_comp().get_animation()) {
1487  anim.my_unit->anim_comp().get_animation()->pause_animation();
1488  }
1489  }
1490 }
1491 
1493 {
1494  for(const auto& anim : animated_units_) {
1495  if(anim.my_unit->anim_comp().get_animation()) {
1496  anim.my_unit->anim_comp().get_animation()->restart_animation();
1497  }
1498  }
1499 }
1500 
1502 {
1503  for(const auto& anim : animated_units_) {
1504  anim.my_unit->anim_comp().set_standing();
1505  }
1506 }
play_controller * controller
Definition: resources.cpp:21
boost::intrusive_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:30
Describes a unit's animation sequence.
Definition: frame.hpp:199
Reserve layers to be selected for WML.
Definition: display.hpp:820
static DIRECTION parse_direction(const std::string &str)
Definition: location.cpp:64
All parameters from a frame at a given instant.
Definition: frame.hpp:33
std::vector< char_t > string
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:87
unit_animation()=delete
std::vector< config::const_all_children_iterator > children
Definition: animation.cpp:54
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:1385
void wait_until(int animation_time) const
Definition: animation.cpp:1419
ter_list read_list(const std::string &str, const ter_layer filler)
Reads a list of terrains from a string, when reading the.
bool invalidate(const map_location &loc)
Function to invalidate a specific tile for redrawing.
Definition: display.cpp:3020
std::vector< hit_type > hits_
Definition: animation.hpp:182
This class represents a single unit of a specific type.
Definition: unit.hpp:100
void set_end_time(int ending_time)
Keep most parameters in a separate class to simplify the handling of the large number of parameters b...
Definition: frame.hpp:140
bool need_update() const
Definition: animation.cpp:959
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
int get_animation_time_potential() const
Definition: animation.cpp:1466
boost::iterator_range< const_all_children_iterator > const_all_children_itors
Definition: config.hpp:664
std::string debug() const
Definition: animation.cpp:1150
Variant for storing WML attributes.
std::vector< map_location::DIRECTION > directions_
Definition: animation.hpp:175
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:166
animation_cursor(const config &cfg)
Definition: animation.cpp:61
bool empty() const
Tests for an attribute that either was never set or was set to "".
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:1327
int get_end_time() const
Definition: animation.cpp:1013
std::vector< config > secondary_attack_filter_
Definition: animation.hpp:181
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:362
int get_begin_time() const
Definition: animation.cpp:1023
void add_frame(int duration, const unit_frame &value, bool force_change=false)
Definition: animation.hpp:52
void clear_haloes()
Definition: animation.cpp:1097
const unit_animation * animation
Definition: animation.hpp:268
int get_animation_time() const
Definition: animation.hpp:75
std::vector< int > value_
Definition: animation.hpp:179
void add_frame(int duration, const unit_frame &value, bool force_change=false)
Adds a frame to an animation.
std::vector< std::string > debug_strings() const
Definition: frame.hpp:236
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:2107
void play_slice(bool is_delay_enabled=true)
#define h
bool animation_finished_potential() const
Definition: animation.cpp:994
map_location::DIRECTION facing() const
The current directin this unit is facing within its hex.
Definition: unit.hpp:1157
particle unit_anim_
Definition: animation.hpp:185
Audio output for sound and music.
Definition: sound.cpp:40
#define d
std::string debug() const
Definition: config.cpp:1211
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
bool empty() const
Definition: config.cpp:830
size_t get_frames_count() const
drawing_layer
The layers to render something on.
Definition: display.hpp:811
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.
bool need_minimal_update() const
Definition: animation.cpp:922
map_location dst_
Definition: animation.hpp:188
int get_begin_time() const
void restart_animation()
Definition: animated.hpp:65
bool need_update() const
Definition: animation.cpp:914
void pause_animation()
Definition: animation.cpp:1065
t_translation::terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition: map.cpp:301
bool would_end() const
Definition: animation.cpp:1409
bool does_not_change() const
Definition: frame.hpp:226
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:624
particle(int start_time=0, const frame_builder &builder=frame_builder())
Definition: animation.hpp:131
const unit_frame & get_last_frame() const
void start_animation(int start_time)
Definition: animation.cpp:1288
void update_last_draw_time(double acceleration=0)
unit_animation * get_animation() const
Get a pointer to the current animation.
void new_animation_frame()
Definition: animated.cpp:30
void restart_animation()
Definition: animation.cpp:1492
int get_end_time() const
Definition: animation.cpp:1471
config & add_child_at(config_key_type key, const config &val, unsigned index)
Definition: config.cpp:507
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.
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:1350
std::set< map_location > overlaped_hex_
Definition: animation.hpp:192
int get_current_frame_begin_time() const
Definition: animation.hpp:100
unit_const_ptr my_unit
Definition: animation.hpp:267
int get_animation_duration() const
std::vector< config > secondary_unit_filter_
Definition: animation.hpp:174
std::vector< std::string > event_
Definition: animation.hpp:178
map_display and display: classes which take care of displaying the map and game-data on the screen...
const T & get_frame(size_t n) const
void redraw(const frame_parameters &value, const map_location &src, const map_location &dst, halo::manager &halo_man)
Definition: animation.cpp:1247
const unit_map & get_units() const
Definition: display.hpp:120
void restart_animation()
Definition: animation.cpp:1074
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:239
const std::string & get_filename() const
Definition: image.hpp:85
int get_end_time() const
double turbo_speed() const
Definition: display.cpp:2393
const std::string & get_modifications() const
Definition: image.hpp:89
unit_animation_component & anim_comp() const
Definition: unit.hpp:1328
animation_branches branches
Definition: animation.cpp:174
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:608
default layer for drawing moving units
Definition: display.hpp:831
friend std::ostream & operator<<(std::ostream &outstream, const unit_animation &u_animation)
Definition: animation.cpp:1157
std::list< animation_branch > animation_branches
Definition: animation.cpp:57
mock_party p
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
default layer for drawing units
Definition: display.hpp:822
void pause_animation()
Definition: animation.cpp:1483
map_location src_
Definition: animation.hpp:187
default layer for missile frames
Definition: display.hpp:836
size_t i
Definition: function.cpp:933
int duration() const
Definition: frame.hpp:221
std::vector< int > value2_
Definition: animation.hpp:183
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
bool does_not_change() const
Definition: frame.cpp:268
bool animation_finished() const
Returns true if the current animation was finished.
std::vector< config > unit_filter_
Definition: animation.hpp:173
config & add_child(config_key_type key)
Definition: config.cpp:475
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:1033
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:888
std::vector< config > primary_attack_filter_
Definition: animation.hpp:180
void set_begin_time(int new_begin_time)
void update_parameters(const map_location &src, const map_location &dst)
Definition: animation.cpp:1059
#define f
std::map< std::string, particle > sub_anims_
Definition: animation.hpp:184
const gamemap & get_map() const
Definition: display.hpp:91
bool find(E event, F functor)
Tests whether an event handler is available.
void set_all_standing()
Definition: animation.cpp:1501
config merge() const
Definition: animation.cpp:43
A variable-expanding proxy for the config class.
Definition: variable.hpp:42
this module manages the cache of images.
Definition: image.cpp:103
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:326
void redraw(frame_parameters &value, halo::manager &halo_man)
Definition: animation.cpp:1083
void update_last_draw_time()
Definition: animation.cpp:1004
unit_iterator find(size_t id)
Definition: map.cpp:311
bool valid() const
Definition: map.hpp:276
int get_animation_time() const
Definition: animation.cpp:1461
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:93
mock_char c
std::shared_ptr< const attack_type > const_attack_ptr
Definition: ptr.hpp:37
bool play_offscreen_
Definition: animation.hpp:191
bool animation_finished_potential() const
bool animation_finished() const
Definition: animation.cpp:984
t_translation::ter_list terrain_types_
Definition: animation.hpp:172
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:67
static rng & default_instance()
Definition: random.cpp:70
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:867
bool need_minimal_update() const
Definition: animation.cpp:969
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
void wait_for_end() const
Definition: animation.cpp:1444
std::set< map_location > get_overlaped_hex(const frame_parameters &value, const map_location &src, const map_location &dst)
Definition: animation.cpp:1276
bool propagate_invalidation(const std::set< map_location > &locs)
If this set is partially invalidated, invalidate all its hexes.
Definition: display.cpp:3047
bool invalidate(frame_parameters &value)
Definition: animation.cpp:1106
void start_animation(int start_time, bool cycles=false)
Starts an animation cycle.