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  , play_offscreen_(true)
272 {
273  add_frame(frame.duration(),frame,!frame.does_not_change());
274 }
275 
276 unit_animation::unit_animation(const config& cfg,const std::string& frame_string )
277  : terrain_types_(t_translation::read_list(cfg["terrain_type"].str()))
278  , unit_filter_()
280  , directions_()
281  , frequency_(cfg["frequency"])
282  , base_score_(cfg["base_score"])
283  , event_()
284  , value_()
287  , hits_()
288  , value2_()
289  , sub_anims_()
290  , unit_anim_(cfg,frame_string)
291  , src_()
292  , dst_()
293  , play_offscreen_(true)
294 {
295  //if(!cfg["debug"].empty()) printf("DEBUG WML: FINAL\n%s\n\n",cfg.debug().c_str());
296 
297  for(const config::any_child& fr : cfg.all_children_range()) {
298  if(fr.key == frame_string) {
299  continue;
300  }
301 
302  if(fr.key.find("_frame", fr.key.size() - 6) == std::string::npos) {
303  continue;
304  }
305 
306  if(sub_anims_.find(fr.key) != sub_anims_.end()) {
307  continue;
308  }
309 
310  sub_anims_[fr.key] = particle(cfg, fr.key.substr(0, fr.key.size() - 5));
311  }
312 
313  event_ = utils::split(cfg["apply_to"]);
314 
315  const std::vector<std::string>& my_directions = utils::split(cfg["direction"]);
316  for(const auto& direction : my_directions) {
318  directions_.push_back(d);
319  }
320 
321  /*const filter_context* fc = game_display::get_singleton();
322  if(!fc) {
323  // This is a pointer to the gamestate. Would prefer to tie unit animations only to the display, but for now this
324  // is an acceptable fallback. It seems to be relevant because when a second game is created, it seems that the
325  // game_display is null at the time that units are being constructed, and hence at the time that this code is running.
326  // A different solution might be to delay the team_builder stage 2 call until after the gui is initialized. Note that
327  // the current set up could conceivably cause problems with the editor, iirc it doesn't initialize a filter context.
328  fc = resources::filter_con;
329  assert(fc);
330  }*/
331 
332  for(const config& filter : cfg.child_range("filter")) {
333  unit_filter_.push_back(filter);
334  }
335 
336  for(const config& filter : cfg.child_range("filter_second")) {
337  secondary_unit_filter_.push_back(filter);
338  }
339 
340  for(const auto& v : utils::split(cfg["value"])) {
341  value_.push_back(atoi(v.c_str()));
342  }
343 
344  for(const auto& h : utils::split(cfg["hits"])) {
345  if(h == "yes" || h == "hit") {
346  hits_.push_back(hit_type::HIT);
347  }
348 
349  if(h == "no" || h == "miss") {
350  hits_.push_back(hit_type::MISS);
351  }
352 
353  if(h == "yes" || h == "kill" ) {
354  hits_.push_back(hit_type::KILL);
355  }
356  }
357 
358  for(const auto& v2 : utils::split(cfg["value_second"])) {
359  value2_.push_back(atoi(v2.c_str()));
360  }
361 
362  for(const config& filter : cfg.child_range("filter_attack")) {
363  primary_attack_filter_.push_back(filter);
364  }
365 
366  for(const config& filter : cfg.child_range("filter_second_attack")) {
367  secondary_attack_filter_.push_back(filter);
368  }
369 
370  play_offscreen_ = cfg["offscreen"].to_bool(true);
371 }
372 
373 int unit_animation::matches(const display& disp, const map_location& loc, const map_location& second_loc,
374  const unit* my_unit, const std::string& event, const int value, hit_type hit, const_attack_ptr attack,
375  const_attack_ptr second_attack, int value2) const
376 {
377  int result = base_score_;
378 
379  if(!event.empty() && !event_.empty()) {
380  if(std::find(event_.begin(), event_.end(), event) == event_.end()) {
381  return MATCH_FAIL;
382  }
383 
384  result++;
385  }
386 
387  if(!terrain_types_.empty()) {
389  return MATCH_FAIL;
390  }
391 
392  result++;
393  }
394 
395  if(!value_.empty()) {
396  if(std::find(value_.begin(), value_.end(), value) == value_.end()) {
397  return MATCH_FAIL;
398  }
399 
400  result++;
401  }
402 
403  if(my_unit) {
404  if(!directions_.empty()) {
405  if(std::find(directions_.begin(), directions_.end(), my_unit->facing()) == directions_.end()) {
406  return MATCH_FAIL;
407  }
408 
409  result++;
410  }
411 
412  for(const auto& filter : unit_filter_) {
413  unit_filter f{ vconfig(filter) };
414  if(!f(*my_unit, loc)) return MATCH_FAIL;
415  ++result;
416  }
417 
418  if(!secondary_unit_filter_.empty()) {
419  unit_map::const_iterator unit = disp.get_units().find(second_loc);
420  if(!unit.valid()) {
421  return MATCH_FAIL;
422  }
423 
424  for(const config& c : secondary_unit_filter_) {
425  unit_filter f{ vconfig(c) };
426  if(!f(*unit, second_loc)) return MATCH_FAIL;
427  result++;
428  }
429  }
430  } else if(!unit_filter_.empty()) {
431  return MATCH_FAIL;
432  }
433 
434  if(frequency_ && !(randomness::rng::default_instance().get_random_int(0, frequency_-1))) {
435  return MATCH_FAIL;
436  }
437 
438  if(!hits_.empty()) {
439  if(std::find(hits_.begin(),hits_.end(),hit) == hits_.end()) {
440  return MATCH_FAIL;
441  }
442 
443  result ++;
444  }
445 
446  if(!value2_.empty()) {
447  if(std::find(value2_.begin(),value2_.end(),value2) == value2_.end()) {
448  return MATCH_FAIL;
449  }
450 
451  result ++;
452  }
453 
454  if(!attack) {
455  if(!primary_attack_filter_.empty()) {
456  return MATCH_FAIL;
457  }
458  }
459 
460  for(const auto& iter : primary_attack_filter_) {
461  if(!attack->matches_filter(iter)) return MATCH_FAIL;
462  result++;
463  }
464 
465  if(!second_attack) {
466  if(!secondary_attack_filter_.empty()) {
467  return MATCH_FAIL;
468  }
469  }
470 
471  for(const auto& iter : secondary_attack_filter_) {
472  if(!second_attack->matches_filter(iter)) return MATCH_FAIL;
473  result++;
474  }
475 
476  return result;
477 }
478 
479 void unit_animation::fill_initial_animations(std::vector<unit_animation>& animations, const config& cfg)
480 {
481  add_anims(animations, cfg);
482 
483  std::vector<unit_animation> animation_base;
484  for(const auto& anim : animations) {
485  if(std::find(anim.event_.begin(), anim.event_.end(), "default") != anim.event_.end()) {
486  animation_base.push_back(anim);
487  animation_base.back().base_score_ += unit_animation::DEFAULT_ANIM;
488  animation_base.back().event_.clear();
489  }
490  }
491 
492  const std::string default_image = cfg["image"];
493 
494  if(animation_base.empty()) {
495  animation_base.push_back(unit_animation(0, frame_builder().image(default_image).duration(1), "", unit_animation::DEFAULT_ANIM));
496  }
497 
498  animations.push_back(unit_animation(0, frame_builder().image(default_image).duration(1), "_disabled_", 0));
499  animations.push_back(unit_animation(0,
500  frame_builder().image(default_image).duration(300).blend("0.0~0.3:100,0.3~0.0:200", {255,255,255}),
501  "_disabled_selected_", 0));
502 
503  for(const auto& base : animation_base) {
504  animations.push_back(base);
505  animations.back().event_ = { "standing" };
506  animations.back().play_offscreen_ = false;
507 
508  animations.push_back(base);
509  animations.back().event_ = { "_ghosted_" };
510  animations.back().unit_anim_.override(0, animations.back().unit_anim_.get_animation_duration(),particle::UNSET,"0.9", "", {0,0,0}, "", "", "~GS()");
511 
512  animations.push_back(base);
513  animations.back().event_ = { "_disabled_ghosted_" };
514  animations.back().unit_anim_.override(0, 1, particle::UNSET, "0.4", "", {0,0,0}, "", "", "~GS()");
515 
516  animations.push_back(base);
517  animations.back().event_ = { "selected" };
518  animations.back().unit_anim_.override(0, 300, particle::UNSET, "", "0.0~0.3:100,0.3~0.0:200", {255,255,255});
519 
520  animations.push_back(base);
521  animations.back().event_ = { "recruited" };
522  animations.back().unit_anim_.override(0, 600, particle::NO_CYCLE, "0~1:600");
523 
524  animations.push_back(base);
525  animations.back().event_ = { "levelin" };
526  animations.back().unit_anim_.override(0, 600, particle::NO_CYCLE, "", "1~0:600", {255,255,255});
527 
528  animations.push_back(base);
529  animations.back().event_ = { "levelout" };
530  animations.back().unit_anim_.override(0, 600, particle::NO_CYCLE, "", "0~1:600,1", {255,255,255});
531 
532  animations.push_back(base);
533  animations.back().event_ = { "pre_movement" };
534  animations.back().unit_anim_.override(0, 1, particle::NO_CYCLE);
535 
536  animations.push_back(base);
537  animations.back().event_ = { "post_movement" };
538  animations.back().unit_anim_.override(0, 1, particle::NO_CYCLE);
539 
540  animations.push_back(base);
541  animations.back().event_ = { "movement" };
542  animations.back().unit_anim_.override(0, 200,
543  particle::NO_CYCLE, "", "", {0,0,0}, "0~1:200", std::to_string(drawing_queue::LAYER_UNIT_MOVE_DEFAULT - drawing_queue::LAYER_UNIT_FIRST));
544 
545  animations.push_back(base);
546  animations.back().event_ = { "defend" };
547  animations.back().unit_anim_.override(0, animations.back().unit_anim_.get_animation_duration(),
548  particle::NO_CYCLE, "", "0.0,0.5:75,0.0:75,0.5:75,0.0", {255,0,0});
549  animations.back().hits_.push_back(hit_type::HIT);
550  animations.back().hits_.push_back(hit_type::KILL);
551 
552  animations.push_back(base);
553  animations.back().event_ = { "defend" };
554 
555  animations.push_back(base);
556  animations.back().event_ = { "attack" };
557  animations.back().unit_anim_.override(-150, 300, particle::NO_CYCLE, "", "", {0,0,0}, "0~0.6:150,0.6~0:150", std::to_string(drawing_queue::LAYER_UNIT_MOVE_DEFAULT-drawing_queue::LAYER_UNIT_FIRST));
558  animations.back().primary_attack_filter_.emplace_back("range", "melee");
559 
560  animations.push_back(base);
561  animations.back().event_ = { "attack" };
562  animations.back().unit_anim_.override(-150, 150, particle::NO_CYCLE);
563  animations.back().primary_attack_filter_.emplace_back("range", "ranged");
564 
565  animations.push_back(base);
566  animations.back().event_ = { "death" };
567  animations.back().unit_anim_.override(0, 600, particle::NO_CYCLE, "1~0:600");
568  animations.back().sub_anims_["_death_sound"] = particle();
569  animations.back().sub_anims_["_death_sound"].add_frame(1, frame_builder().sound(cfg["die_sound"]), true);
570 
571  animations.push_back(base);
572  animations.back().event_ = { "victory" };
573  animations.back().unit_anim_.override(0, animations.back().unit_anim_.get_animation_duration(), particle::CYCLE);
574 
575  animations.push_back(base);
576  animations.back().unit_anim_.override(0, 150, particle::NO_CYCLE, "1~0:150");
577  animations.back().event_ = { "pre_teleport" };
578 
579  animations.push_back(base);
580  animations.back().unit_anim_.override(0, 150, particle::NO_CYCLE, "0~1:150,1");
581  animations.back().event_ = { "post_teleport" };
582 
583  animations.push_back(base);
584  animations.back().event_ = { "healing" };
585 
586  animations.push_back(base);
587  animations.back().event_ = { "healed" };
588  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});
589 
590  const std::string healed_sound = get_heal_sound(cfg);
591 
592  animations.back().sub_anims_["_healed_sound"].add_frame(1, frame_builder().sound(healed_sound), true);
593 
594  animations.push_back(base);
595  animations.back().event_ = { "poisoned" };
596  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});
597  animations.back().sub_anims_["_poison_sound"] = particle();
598  animations.back().sub_anims_["_poison_sound"].add_frame(1, frame_builder().sound(game_config::sounds::status::poisoned), true);
599  }
600 }
601 
602 static void add_simple_anim(std::vector<unit_animation>& animations,
603  const config& cfg, char const* tag_name, char const* apply_to,
605  bool offscreen = true)
606 {
607  for(const animation_branch& ab : prepare_animation(cfg, tag_name)) {
608  config anim = ab.merge();
609  anim["apply_to"] = apply_to;
610 
611  if(!offscreen) {
612  config::attribute_value& v = anim["offscreen"];
613  if(v.empty()) v = false;
614  }
615 
616  config::attribute_value& v = anim["layer"];
617  if(v.empty()) v = layer - drawing_queue::LAYER_UNIT_FIRST;
618 
619  animations.emplace_back(anim);
620  }
621 }
622 
623 void unit_animation::add_anims( std::vector<unit_animation> & animations, const config & cfg)
624 {
625  for(const animation_branch& ab : prepare_animation(cfg, "animation")) {
626  animations.emplace_back(ab.merge());
627  }
628 
632 
633  add_simple_anim(animations, cfg, "resistance_anim", "resistance");
634  add_simple_anim(animations, cfg, "leading_anim", "leading");
635  add_simple_anim(animations, cfg, "recruit_anim", "recruited");
636  add_simple_anim(animations, cfg, "recruiting_anim", "recruiting");
637  add_simple_anim(animations, cfg, "idle_anim", "idling", drawing_queue::LAYER_UNIT_DEFAULT, false);
638  add_simple_anim(animations, cfg, "levelin_anim", "levelin");
639  add_simple_anim(animations, cfg, "levelout_anim", "levelout");
640 
641  for(const animation_branch& ab : prepare_animation(cfg, "standing_anim")) {
642  config anim = ab.merge();
643  anim["apply_to"] = "standing";
644  anim["cycles"] = true;
645 
646  // Add cycles to all frames within a standing animation block
647  for(config::const_all_children_iterator ci : ab.children) {
648  std::string sub_frame_name = ci->key;
649  std::size_t pos = sub_frame_name.find("_frame");
650  if(pos != std::string::npos) {
651  anim[sub_frame_name.substr(0, pos) + "_cycles"] = true;
652  }
653  }
654 
655  if(anim["layer"].empty()) {
656  anim["layer"] = default_layer;
657  }
658 
659  if(anim["offscreen"].empty()) {
660  anim["offscreen"] = false;
661  }
662 
663  animations.emplace_back(anim);
664  }
665 
666  // Standing animations are also used as default animations
667  for(const animation_branch& ab : prepare_animation(cfg, "standing_anim")) {
668  config anim = ab.merge();
669  anim["apply_to"] = "default";
670  anim["cycles"] = true;
671 
672  for(config::const_all_children_iterator ci : ab.children) {
673  std::string sub_frame_name = ci->key;
674  std::size_t pos = sub_frame_name.find("_frame");
675  if(pos != std::string::npos) {
676  anim[sub_frame_name.substr(0, pos) + "_cycles"] = true;
677  }
678  }
679 
680  if(anim["layer"].empty()) {
681  anim["layer"] = default_layer;
682  }
683 
684  if(anim["offscreen"].empty()) {
685  anim["offscreen"] = false;
686  }
687 
688  animations.emplace_back(anim);
689  }
690 
691  for(const animation_branch& ab : prepare_animation(cfg, "healing_anim")) {
692  config anim = ab.merge();
693  anim["apply_to"] = "healing";
694  anim["value"] = anim["damage"];
695 
696  if(anim["layer"].empty()) {
697  anim["layer"] = default_layer;
698  }
699 
700  animations.emplace_back(anim);
701  }
702 
703  for(const animation_branch& ab : prepare_animation(cfg, "healed_anim")) {
704  config anim = ab.merge();
705  anim["apply_to"] = "healed";
706  anim["value"] = anim["healing"];
707 
708  if(anim["layer"].empty()) {
709  anim["layer"] = default_layer;
710  }
711 
712  animations.emplace_back(anim);
713  animations.back().sub_anims_["_healed_sound"] = particle();
714 
715  const std::string healed_sound = get_heal_sound(cfg);
716  animations.back().sub_anims_["_healed_sound"].add_frame(1,frame_builder().sound(healed_sound),true);
717  }
718 
719  for(const animation_branch &ab : prepare_animation(cfg, "poison_anim")) {
720  config anim = ab.merge();
721  anim["apply_to"] = "poisoned";
722  anim["value"] = anim["damage"];
723 
724  if(anim["layer"].empty()) {
725  anim["layer"] = default_layer;
726  }
727 
728  animations.emplace_back(anim);
729  animations.back().sub_anims_["_poison_sound"] = particle();
730  animations.back().sub_anims_["_poison_sound"].add_frame(1,frame_builder().sound(game_config::sounds::status::poisoned),true);
731  }
732 
733  add_simple_anim(animations, cfg, "pre_movement_anim", "pre_movement", drawing_queue::LAYER_UNIT_MOVE_DEFAULT);
734 
735  for(const animation_branch& ab : prepare_animation(cfg, "movement_anim")) {
736  config anim = ab.merge();
737  anim["apply_to"] = "movement";
738 
739  if(anim["offset"].empty()) {
740  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,";
741  }
742 
743  if(anim["layer"].empty()) {
744  anim["layer"] = move_layer;
745  }
746 
747  animations.emplace_back(anim);
748  }
749 
750  add_simple_anim(animations, cfg, "post_movement_anim", "post_movement", drawing_queue::LAYER_UNIT_MOVE_DEFAULT);
751 
752  for(const animation_branch& ab : prepare_animation(cfg, "defend")) {
753  config anim = ab.merge();
754  anim["apply_to"] = "defend";
755 
756  if(anim["layer"].empty()) {
757  anim["layer"] = default_layer;
758  }
759 
760  if(!anim["damage"].empty() && anim["value"].empty()) {
761  anim["value"] = anim["damage"];
762  }
763 
764  if(anim["hits"].empty()) {
765  anim["hits"] = false;
766  animations.emplace_back(anim);
767  animations.back().base_score_--; //so default doesn't interfere with 'if' block
768 
769  anim["hits"] = true;
770  animations.emplace_back(anim);
771  animations.back().base_score_--;
772 
773  image::locator image_loc = animations.back().get_last_frame().end_parameters().image;
774  animations.back().add_frame(225, frame_builder()
775  .image(image_loc.get_filename()+image_loc.get_modifications())
776  .duration(225)
777  .blend("0.0,0.5:75,0.0:75,0.5:75,0.0", {255,0,0}));
778  } else {
779  for(const std::string& hit_type : utils::split(anim["hits"])) {
780  config tmp = anim;
781  tmp["hits"] = hit_type;
782 
783  animations.emplace_back(tmp);
784 
785  image::locator image_loc = animations.back().get_last_frame().end_parameters().image;
786  if(hit_type == "yes" || hit_type == "hit" || hit_type=="kill") {
787  animations.back().add_frame(225, frame_builder()
788  .image(image_loc.get_filename() + image_loc.get_modifications())
789  .duration(225)
790  .blend("0.0,0.5:75,0.0:75,0.5:75,0.0", {255,0,0}));
791  }
792  }
793  }
794  }
795 
796  add_simple_anim(animations, cfg, "draw_weapon_anim", "draw_weapon", drawing_queue::LAYER_UNIT_MOVE_DEFAULT);
797  add_simple_anim(animations, cfg, "sheath_weapon_anim", "sheath_weapon", drawing_queue::LAYER_UNIT_MOVE_DEFAULT);
798 
799  for(const animation_branch& ab : prepare_animation(cfg, "attack_anim")) {
800  config anim = ab.merge();
801  anim["apply_to"] = "attack";
802 
803  if(anim["layer"].empty()) {
804  anim["layer"] = move_layer;
805  }
806 
807  config::const_child_itors missile_fs = anim.child_range("missile_frame");
808  if(anim["offset"].empty() && missile_fs.empty()) {
809  anim["offset"] ="0~0.6,0.6~0";
810  }
811 
812  if(!missile_fs.empty()) {
813  if(anim["missile_offset"].empty()) {
814  anim["missile_offset"] = "0~0.8";
815  }
816 
817  if(anim["missile_layer"].empty()) {
818  anim["missile_layer"] = missile_layer;
819  }
820 
821  config tmp;
822  tmp["duration"] = 1;
823 
824  anim.add_child("missile_frame", tmp);
825  anim.add_child_at("missile_frame", tmp, 0);
826  }
827 
828  animations.emplace_back(anim);
829  }
830 
831  for(const animation_branch& ab : prepare_animation(cfg, "death")) {
832  config anim = ab.merge();
833  anim["apply_to"] = "death";
834 
835  if(anim["layer"].empty()) {
836  anim["layer"] = default_layer;
837  }
838 
839  animations.emplace_back(anim);
840  image::locator image_loc = animations.back().get_last_frame().end_parameters().image;
841 
842  animations.back().add_frame(600, frame_builder()
843  .image(image_loc.get_filename()+image_loc.get_modifications())
844  .duration(600)
845  .highlight("1~0:600"));
846 
847  if(!cfg["die_sound"].empty()) {
848  animations.back().sub_anims_["_death_sound"] = particle();
849  animations.back().sub_anims_["_death_sound"].add_frame(1,frame_builder().sound(cfg["die_sound"]),true);
850  }
851  }
852 
853  add_simple_anim(animations, cfg, "victory_anim", "victory");
854 
855  for(const animation_branch& ab : prepare_animation(cfg, "extra_anim")) {
856  config anim = ab.merge();
857  anim["apply_to"] = anim["flag"];
858 
859  if(anim["layer"].empty()) {
860  anim["layer"] = default_layer;
861  }
862 
863  animations.emplace_back(anim);
864  }
865 
866  for(const animation_branch& ab : prepare_animation(cfg, "teleport_anim")) {
867  config anim = ab.merge();
868  if(anim["layer"].empty()) {
869  anim["layer"] = default_layer;
870  }
871 
872  anim["apply_to"] = "pre_teleport";
873  animations.emplace_back(anim);
874  animations.back().unit_anim_.set_end_time(0);
875 
876  anim["apply_to"] ="post_teleport";
877  animations.emplace_back(anim);
878  animations.back().unit_anim_.remove_frames_until(0);
879  }
880 }
881 
883  , int duration
884  , const cycle_state cycles
885  , const std::string& highlight
886  , const std::string& blend_ratio
887  , color_t blend_color
888  , const std::string& offset
889  , const std::string& layer
890  , const std::string& modifiers)
891 {
892  set_begin_time(start_time);
893  parameters_.override(duration,highlight,blend_ratio,blend_color,offset,layer,modifiers);
894 
895  if(cycles == CYCLE) {
896  cycles_=true;
897  } else if(cycles==NO_CYCLE) {
898  cycles_=false;
899  }
900 
901  if(get_animation_duration() < duration) {
902  add_frame(duration -get_animation_duration(), get_last_frame());
903  } else if(get_animation_duration() > duration) {
904  set_end_time(duration);
905  }
906 }
907 
909 {
910  if(animated<unit_frame>::need_update()) return true;
911  if(get_current_frame().need_update()) return true;
912  if(parameters_.need_update()) return true;
913  return false;
914 }
915 
917 {
918  return get_current_frame_begin_time() != last_frame_begin_time_;
919 }
920 
921 unit_animation::particle::particle(const config& cfg, const std::string& frame_string)
922  : animated<unit_frame>()
923  , accelerate(true)
924  , parameters_()
925  , halo_id_()
926  , last_frame_begin_time_(0)
927  , cycles_(false)
928 {
929  starting_frame_time_ = INT_MAX;
930 
931  config::const_child_itors range = cfg.child_range(frame_string + "frame");
932  if(!range.empty() && cfg[frame_string + "start_time"].empty()) {
933  for(const config& frame : range) {
934  starting_frame_time_ = std::min(starting_frame_time_, frame["begin"].to_int());
935  }
936  } else {
937  starting_frame_time_ = cfg[frame_string + "start_time"];
938  }
939 
940  for(const config& frame : range) {
941  unit_frame tmp_frame(frame);
942  add_frame(tmp_frame.duration(), tmp_frame, !tmp_frame.does_not_change());
943  }
944 
945  cycles_ = cfg[frame_string + "cycles"].to_bool(false);
947 
949  force_change();
950  }
951 }
952 
954 {
955  if(unit_anim_.need_update()) return true;
956  for(const auto& anim : sub_anims_) {
957  if(anim.second.need_update()) return true;
958  }
959 
960  return false;
961 }
962 
964 {
965  if(!play_offscreen_) {
966  return false;
967  }
968 
969  if(unit_anim_.need_minimal_update()) return true;
970 
971  for(const auto& anim : sub_anims_) {
972  if(anim.second.need_minimal_update()) return true;
973  }
974 
975  return false;
976 }
977 
979 {
980  if(!unit_anim_.animation_finished()) return false;
981  for(const auto& anim : sub_anims_) {
982  if(!anim.second.animation_finished()) return false;
983  }
984 
985  return true;
986 }
987 
989 {
990  if(!unit_anim_.animation_finished_potential()) return false;
991  for(const auto& anim : sub_anims_) {
992  if(!anim.second.animation_finished_potential()) return false;
993  }
994 
995  return true;
996 }
997 
999 {
1000  double acceleration = unit_anim_.accelerate ? display::get_singleton()->turbo_speed() : 1.0;
1001  unit_anim_.update_last_draw_time(acceleration);
1002  for(auto& anim : sub_anims_) {
1003  anim.second.update_last_draw_time(acceleration);
1004  }
1005 }
1006 
1008 {
1009  int result = unit_anim_.get_end_time();
1010  for(const auto& anim : sub_anims_) {
1011  result = std::max<int>(result, anim.second.get_end_time());
1012  }
1013 
1014  return result;
1015 }
1016 
1018 {
1019  int result = unit_anim_.get_begin_time();
1020  for(const auto& anim : sub_anims_) {
1021  result = std::min<int>(result, anim.second.get_begin_time());
1022  }
1023 
1024  return result;
1025 }
1026 
1028  , const map_location& src
1029  , const map_location& dst
1030  , const std::string& text
1031  , const color_t text_color
1032  , const bool accelerate)
1033 {
1035  src_ = src;
1036  dst_ = dst;
1037 
1038  unit_anim_.start_animation(start_time);
1039 
1040  if(!text.empty()) {
1041  particle crude_build;
1042  crude_build.add_frame(1, frame_builder());
1043  crude_build.add_frame(1, frame_builder().text(text, text_color), true);
1044  sub_anims_["_add_text"] = crude_build;
1045  }
1046 
1047  for(auto& anim : sub_anims_) {
1048  anim.second.accelerate = accelerate;
1049  anim.second.start_animation(start_time);
1050  }
1051 }
1052 
1054 {
1055  src_ = src;
1056  dst_ = dst;
1057 }
1058 
1060 {
1062 
1063  for(auto& anim : sub_anims_) {
1064  anim.second.pause_animation();
1065  }
1066 }
1067 
1069 {
1071 
1072  for(auto& anim : sub_anims_) {
1073  anim.second.restart_animation();
1074  }
1075 }
1076 
1078 {
1079  value.primary_frame = true;
1080  unit_anim_.redraw(value,src_,dst_, halo_man);
1081 
1082  value.primary_frame = false;
1083  for(auto& anim : sub_anims_) {
1084  anim.second.redraw(value, src_, dst_, halo_man);
1085  }
1086 }
1087 
1089 {
1091 
1092  for(auto& anim : sub_anims_) {
1093  anim.second.clear_halo();
1094  }
1095 }
1096 
1097 std::string unit_animation::debug() const
1098 {
1099  std::ostringstream outstream;
1100  outstream << *this;
1101  return outstream.str();
1102 }
1103 
1104 std::ostream& operator<<(std::ostream& outstream, const unit_animation& u_animation)
1105 {
1106  std::string events_string = utils::join(u_animation.event_);
1107  outstream << "[" << events_string << "]\n";
1108 
1109  outstream << "\tstart_time=" << u_animation.get_begin_time() << '\n';
1110 
1111  if(u_animation.hits_.size() > 0) {
1112  std::vector<std::string> hits;
1113  std::transform(u_animation.hits_.begin(), u_animation.hits_.end(), std::back_inserter(hits), unit_animation::hit_type::enum_to_string);
1114  outstream << "\thits=" << utils::join(hits) << '\n';
1115  }
1116 
1117  if(u_animation.directions_.size() > 0) {
1118  std::vector<std::string> dirs;
1119  std::transform(u_animation.directions_.begin(), u_animation.directions_.end(), std::back_inserter(dirs), map_location::write_direction);
1120  outstream << "\tdirections=" << utils::join(dirs) << '\n';
1121  }
1122 
1123  if(u_animation.terrain_types_.size() > 0) {
1124  outstream << "\tterrain=" << utils::join(u_animation.terrain_types_) << '\n';
1125  }
1126 
1127  if(u_animation.frequency_ > 0) outstream << "frequency=" << u_animation.frequency_ << '\n';
1128 
1129  if(u_animation.unit_filter_.size() > 0) {
1130  outstream << "[filter]\n";
1131  for(const config& cfg : u_animation.unit_filter_) {
1132  outstream << cfg.debug();
1133  }
1134 
1135  outstream << "[/filter]\n";
1136  }
1137 
1138  if(u_animation.secondary_unit_filter_.size() > 0) {
1139  outstream << "[filter_second]\n";
1140  for(const config& cfg : u_animation.secondary_unit_filter_) {
1141  outstream << cfg.debug();
1142  }
1143 
1144  outstream << "[/filter_second]\n";
1145  }
1146 
1147  if(u_animation.primary_attack_filter_.size() > 0) {
1148  outstream << "[filter_attack]\n";
1149  for(const config cfg : u_animation.primary_attack_filter_) {
1150  outstream << cfg.debug();
1151  }
1152 
1153  outstream << "[/filter_attack]\n";
1154  }
1155 
1156  if(u_animation.secondary_attack_filter_.size() > 0) {
1157  outstream << "[filter_second_attack]\n";
1158  for(const config cfg : u_animation.secondary_attack_filter_) {
1159  outstream << cfg.debug();
1160  }
1161 
1162  outstream << "[/filter_second_attack]\n";
1163  }
1164 
1165  for(std::size_t i = 0; i < u_animation.unit_anim_.get_frames_count(); i++) {
1166  outstream << "\t[frame]\n";
1167  for(const std::string frame_string : u_animation.unit_anim_.get_frame(i).debug_strings()) {
1168  outstream << "\t\t" << frame_string <<"\n";
1169  }
1170  outstream << "\t[/frame]\n";
1171  }
1172 
1173  for(std::pair<std::string, unit_animation::particle> p : u_animation.sub_anims_) {
1174  for(std::size_t i = 0; i < p.second.get_frames_count(); i++) {
1175  std::string sub_frame_name = p.first;
1176  std::size_t pos = sub_frame_name.find("_frame");
1177  if(pos != std::string::npos) sub_frame_name = sub_frame_name.substr(0, pos);
1178 
1179  outstream << "\t" << sub_frame_name << "_start_time=" << p.second.get_begin_time() << '\n';
1180  outstream << "\t[" << p.first << "]\n";
1181 
1182  for(const std::string frame_string : p.second.get_frame(i).debug_strings()) {
1183  outstream << "\t\t" << frame_string << '\n';
1184  }
1185 
1186  outstream << "\t[/" << p.first << "]\n";
1187  }
1188  }
1189 
1190  outstream << "[/" << events_string << "]\n";
1191  return outstream;
1192 }
1193 
1195 {
1196  const unit_frame& current_frame = get_current_frame();
1197  const int animation_time = get_animation_time();
1198  const frame_parameters default_val = parameters_.parameters(animation_time - get_begin_time());
1199 
1200  // Everything is relative to the first frame in an attack/defense/etc. block.
1201  // so we need to check if this particular frame is due to be shown at this time
1202  bool in_scope_of_frame = (animation_time >= get_current_frame_begin_time() ? true: false);
1203  if(animation_time > get_current_frame_end_time()) in_scope_of_frame = false;
1204 
1205  // Sometimes even if the frame is not due to be shown, a frame image still must be shown.
1206  // i.e. in a defense animation that is shorter than an attack animation.
1207  // the halos should not persist though and use the 'in_scope_of_frame' variable.
1208 
1209  // For sound frames we want the first time variable set only after the frame has started.
1212  current_frame.redraw(get_current_frame_time(), true, in_scope_of_frame, src, dst, halo_id_, halo_man, default_val, value);
1213  } else {
1214  current_frame.redraw(get_current_frame_time(), false, in_scope_of_frame, src, dst, halo_id_, halo_man, default_val, value);
1215  }
1216 }
1217 
1219 {
1220  halo_id_.reset();
1221 }
1222 
1223 std::set<map_location> unit_animation::particle::get_overlaped_hex(const frame_parameters& value, const map_location& src, const map_location& dst)
1224 {
1225  const unit_frame& current_frame = get_current_frame();
1227  return current_frame.get_overlaped_hex(get_current_frame_time(), src, dst, default_val,value);
1228 }
1229 
1231 {
1232  halo_id_.reset();
1233 }
1234 
1236 {
1237  halo_id_.reset();
1241 }
1242 
1243 void unit_animator::add_animation(const unit* animated_unit
1244  , const std::string& event
1245  , const map_location &src
1246  , const map_location &dst
1247  , const int value
1248  , bool with_bars
1249  , const std::string& text
1250  , const color_t text_color
1251  , const unit_animation::hit_type hit_type
1252  , const_attack_ptr attack
1253  , const_attack_ptr second_attack
1254  , int value2)
1255 {
1256  if(!animated_unit) return;
1257 
1258  display* disp = display::get_singleton();
1259 
1260  anim_elem tmp;
1261  tmp.my_unit = unit_const_ptr(animated_unit);
1262  tmp.text = text;
1263  tmp.text_color = text_color;
1264  tmp.src = src;
1265  tmp.with_bars= with_bars;
1266  tmp.animation = animated_unit->anim_comp().choose_animation(*disp, src, event, dst, value, hit_type, attack, second_attack, value2);
1267 
1268  if(!tmp.animation) return;
1269 
1270  start_time_ = std::max<int>(start_time_, tmp.animation->get_begin_time());
1271  animated_units_.push_back(std::move(tmp));
1272 }
1273 
1274 void unit_animator::add_animation(const unit* animated_unit
1275  , const unit_animation* anim
1276  , const map_location &src
1277  , bool with_bars
1278  , const std::string& text
1279  , const color_t text_color)
1280 {
1281  if(!animated_unit) return;
1282 
1283  anim_elem tmp;
1284  tmp.my_unit = unit_const_ptr(animated_unit);
1285  tmp.text = text;
1286  tmp.text_color = text_color;
1287  tmp.src = src;
1288  tmp.with_bars = with_bars;
1289  tmp.animation = anim;
1290 
1291  if(!tmp.animation) return;
1292 
1293  start_time_ = std::max<int>(start_time_, tmp.animation->get_begin_time());
1294  animated_units_.push_back(std::move(tmp));
1295 }
1296 
1298  , const std::string& event
1299  , const map_location &src
1300  , const map_location & dst
1301  , const int value
1302  , bool with_bars
1303  , const std::string& text
1304  , const color_t text_color
1305  , const unit_animation::hit_type hit_type
1306  , const_attack_ptr attack
1307  , const_attack_ptr second_attack
1308  , int value2)
1309 {
1310  if(!animated_unit) return;
1311 
1312  display* disp = display::get_singleton();
1313  if(animated_unit->anim_comp().get_animation() &&
1314  !animated_unit->anim_comp().get_animation()->animation_finished_potential() &&
1315  animated_unit->anim_comp().get_animation()->matches(
1316  *disp, src, dst, animated_unit, event, value, hit_type, attack, second_attack, value2) > unit_animation::MATCH_FAIL)
1317  {
1318  anim_elem tmp;
1319  tmp.my_unit = unit_const_ptr(animated_unit);
1320  tmp.text = text;
1321  tmp.text_color = text_color;
1322  tmp.src = src;
1323  tmp.with_bars= with_bars;
1324  tmp.animation = nullptr;
1325 
1326  animated_units_.push_back(std::move(tmp));
1327  } else {
1328  add_animation(animated_unit,event,src,dst,value,with_bars,text,text_color,hit_type,attack,second_attack,value2);
1329  }
1330 }
1331 
1333 {
1334  int begin_time = INT_MAX;
1335 
1336  for(const auto& anim : animated_units_) {
1337  if(anim.my_unit->anim_comp().get_animation()) {
1338  if(anim.animation) {
1339  begin_time = std::min<int>(begin_time, anim.animation->get_begin_time());
1340  } else {
1341  begin_time = std::min<int>(begin_time, anim.my_unit->anim_comp().get_animation()->get_begin_time());
1342  }
1343  }
1344  }
1345 
1346  for(auto& anim : animated_units_) {
1347  if(anim.animation) {
1348  anim.my_unit->anim_comp().start_animation(begin_time, anim.animation, anim.with_bars, anim.text, anim.text_color);
1349  anim.animation = nullptr;
1350  } else {
1351  anim.my_unit->anim_comp().get_animation()->update_parameters(anim.src, anim.src.get_direction(anim.my_unit->facing()));
1352  }
1353  }
1354 }
1355 
1357 {
1358  bool finished = true;
1359  for(const auto& anim : animated_units_) {
1360  finished &= anim.my_unit->anim_comp().get_animation()->animation_finished_potential();
1361  }
1362 
1363  return finished;
1364 }
1365 
1366 void unit_animator::wait_until(int animation_time) const
1367 {
1368  // important to set a max animation time so that the time does not go past this value for movements.
1369  // fix for bug #1565
1370  animated_units_[0].my_unit->anim_comp().get_animation()->set_max_animation_time(animation_time);
1371 
1372  display* disp = display::get_singleton();
1373  double speed = disp->turbo_speed();
1374 
1376 
1377  int end_tick = animated_units_[0].my_unit->anim_comp().get_animation()->time_to_tick(animation_time);
1378  while(SDL_GetTicks() < static_cast<unsigned int>(end_tick) - std::min<int>(static_cast<unsigned int>(20 / speed), 20)) {
1379  CVideo::delay(std::max<int>(0, std::min<int>(10, static_cast<int>((animation_time - get_animation_time()) * speed))));
1380 
1382  end_tick = animated_units_[0].my_unit->anim_comp().get_animation()->time_to_tick(animation_time);
1383  }
1384 
1385  CVideo::delay(std::max<int>(0, end_tick - SDL_GetTicks() + 5));
1386 
1388  animated_units_[0].my_unit->anim_comp().get_animation()->set_max_animation_time(0);
1389 }
1390 
1392 {
1393  if(game_config::no_delay) return;
1394 
1395  bool finished = false;
1396  while(!finished) {
1398 
1399  CVideo::delay(10);
1400 
1401  finished = true;
1402  for(const auto& anim : animated_units_) {
1403  finished &= anim.my_unit->anim_comp().get_animation()->animation_finished_potential();
1404  }
1405  }
1406 }
1407 
1409 {
1410  return animated_units_[0].my_unit->anim_comp().get_animation()->get_animation_time() ;
1411 }
1412 
1414 {
1415  return animated_units_[0].my_unit->anim_comp().get_animation()->get_animation_time_potential() ;
1416 }
1417 
1419 {
1420  int end_time = INT_MIN;
1421  for(const auto& anim : animated_units_) {
1422  if(anim.my_unit->anim_comp().get_animation()) {
1423  end_time = std::max<int>(end_time, anim.my_unit->anim_comp().get_animation()->get_end_time());
1424  }
1425  }
1426 
1427  return end_time;
1428 }
1429 
1431 {
1432  for(const auto& anim : animated_units_) {
1433  if(anim.my_unit->anim_comp().get_animation()) {
1434  anim.my_unit->anim_comp().get_animation()->pause_animation();
1435  }
1436  }
1437 }
1438 
1440 {
1441  for(const auto& anim : animated_units_) {
1442  if(anim.my_unit->anim_comp().get_animation()) {
1443  anim.my_unit->anim_comp().get_animation()->restart_animation();
1444  }
1445  }
1446 }
1447 
1449 {
1450  for(const auto& anim : animated_units_) {
1451  anim.my_unit->anim_comp().set_standing();
1452  }
1453 }
const frame_parameters parameters(int current_time) const
Getters for the different parameters.
Definition: frame.cpp:298
play_controller * controller
Definition: resources.cpp:21
boost::intrusive_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:30
Reserve layers to be selected for WML.
void wait_for_end() const
Definition: animation.cpp:1391
const std::string & get_modifications() const
Definition: picture.hpp:91
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
int get_current_frame_end_time() const
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:689
std::size_t get_frames_count() const
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
Default layer for missile frames.
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:99
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:874
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:1332
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_begin_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:631
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:1356
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:1274
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
static void add_simple_anim(std::vector< unit_animation > &animations, const config &cfg, char const *tag_name, char const *apply_to, drawing_queue::layer layer=drawing_queue::LAYER_UNIT_DEFAULT, bool offscreen=true)
Definition: animation.cpp:602
bool animation_finished_potential() const
void add_frame(int duration, const unit_frame &value, bool force_change=false)
Definition: animation.hpp:53
void clear_haloes()
Definition: animation.cpp:1088
const unit_animation * animation
Definition: animation.hpp:267
std::vector< int > value_
Definition: animation.hpp:180
const unit_map & get_units() const
Definition: display.hpp:157
map_location::DIRECTION facing() const
The current directin this unit is facing within its hex.
Definition: unit.hpp:1190
virtual void play_slice(bool is_delay_enabled=true)
int get_begin_time() const
Definition: animation.cpp:1017
#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:953
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:328
bool need_update() const
Definition: animation.cpp:908
int get_end_time() const
Definition: animation.cpp:1007
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:916
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:237
map_location dst_
Definition: animation.hpp:189
int get_animation_time_potential() const
Definition: animation.cpp:1413
t_translation::terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition: map.cpp:292
void pause_animation()
Definition: animation.cpp:1059
void wait_until(int animation_time) const
Definition: animation.cpp:1366
int get_animation_duration() const
int get_begin_time() const
int get_current_frame_time() const
particle(int start_time=0, const frame_builder &builder=frame_builder())
Definition: animation.hpp:132
layer
The various map rendering layers.
void start_animation(int start_time)
Definition: animation.cpp:1235
double turbo_speed() const
Definition: display.cpp:1382
void new_animation_frame()
Definition: animated.cpp:30
void restart_animation()
Definition: animation.cpp:1439
const T & get_frame(std::size_t n) const
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.
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:1297
bool does_not_change() const
Definition: frame.hpp:227
unit_const_ptr my_unit
Definition: animation.hpp:266
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:1194
all_children_iterator erase(const all_children_iterator &i)
Definition: config.cpp:642
const unit_frame & get_last_frame() const
Definition: animation.hpp:48
void restart_animation()
Definition: animation.cpp:1068
Encapsulates the map of the game.
Definition: location.hpp:42
boost::iterator_range< const_child_iterator > const_child_itors
Definition: config.hpp:210
void restart_animation()
Definition: animated.hpp:55
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:483
std::size_t i
Definition: function.cpp:933
unit_animation_component & anim_comp() const
Definition: unit.hpp:1361
animation_branches branches
Definition: animation.cpp:174
int get_end_time() const
Definition: animation.cpp:1418
const unit_frame & get_current_frame() const
friend std::ostream & operator<<(std::ostream &outstream, const unit_animation &u_animation)
Definition: animation.cpp:1104
std::list< animation_branch > animation_branches
Definition: animation.cpp:57
mock_party p
void update_last_draw_time(double acceleration=0)
void pause_animation()
Definition: animation.cpp:1430
map_location src_
Definition: animation.hpp:188
void start_animation(int start_time, bool cycles=false)
Starts an animation cycle.
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
default layer for drawing units
const std::string & get_filename() const
Definition: picture.hpp:86
std::vector< config > unit_filter_
Definition: animation.hpp:174
config & add_child(config_key_type key)
Definition: config.cpp:479
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:1027
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:882
std::vector< config > primary_attack_filter_
Definition: animation.hpp:181
int get_animation_time() const
Definition: animation.cpp:1408
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:373
const gamemap & get_map() const
Definition: display.hpp:109
void update_parameters(const map_location &src, const map_location &dst)
Definition: animation.cpp:1053
#define f
std::map< std::string, particle > sub_anims_
Definition: animation.hpp:185
std::string debug() const
Definition: animation.cpp:1097
void set_all_standing()
Definition: animation.cpp:1448
Default layer for drawing moving units.
A variable-expanding proxy for the config class.
Definition: variable.hpp:42
void pause_animation()
Definition: animated.hpp:50
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:235
void redraw(frame_parameters &value, halo::manager &halo_man)
Definition: animation.cpp:1077
void update_last_draw_time()
Definition: animation.cpp:998
bool need_minimal_update() const
Definition: animation.cpp:963
int get_end_time() const
bool animation_finished_potential() const
Definition: animation.cpp:988
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
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:191
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:274
bool empty() const
Definition: config.cpp:837
bool animation_finished() const
Returns true if the current animation was finished.
std::string debug() const
Definition: config.cpp:1230
std::set< map_location > get_overlaped_hex(const frame_parameters &value, const map_location &src, const map_location &dst)
Definition: animation.cpp:1223
bool animation_finished() const
Definition: animation.cpp:978
int get_animation_time() const