The Battle for Wesnoth  1.17.0-dev
animation.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2021
3  by Jeremy Rosen <jeremy.rosen@enst-bretagne.fr>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #include "units/animation.hpp"
17 
18 #include "display.hpp"
19 #include "filter_context.hpp"
20 #include "map/map.hpp"
21 #include "play_controller.hpp"
22 #include "resources.hpp"
23 #include "color.hpp"
24 #include "units/unit.hpp"
26 #include "units/filter.hpp"
27 #include "variable.hpp"
28 #include "random.hpp"
29 
30 #include <algorithm>
31 
32 static std::string get_heal_sound(const config& cfg)
33 {
34  return cfg["healed_sound"].empty() ? "heal.wav" : cfg["healed_sound"].str();
35 }
36 
38 {
40  : attributes()
41  , children()
42  {}
43 
44  config merge() const
45  {
46  config result = attributes;
48  result.add_child(i->key, i->cfg);
49  }
50 
51  return result;
52  }
53 
55  std::vector<config::const_all_children_iterator> children;
56 };
57 
58 typedef std::list<animation_branch> animation_branches;
59 
61 {
63  : itors(cfg.all_children_range()), branches(1), parent(nullptr)
64  {
65  branches.back().attributes.merge_attributes(cfg);
66  }
67 
69  : itors(cfg.all_children_range()), branches(p->branches), parent(p)
70  {
71  // If similar 'if' condition in parent branches, we need to
72  // cull the branches where there are partial matches.
73  // Hence the need to check if the condition has come up before.
74  // Also, the attributes are merged here between branches.
75  bool previously_hits_set = false;
76  bool previously_direction_set = false;
77  bool previously_terrain_set = false;
78  bool previously_value_set = false;
79  bool previously_value_2nd_set = false;
80 
81  const std::string s_cfg_hits = cfg["hits"];
82  const std::string s_cfg_direction = cfg["direction"];
83  const std::string s_cfg_terrain = cfg["terrain_types"];
84  const std::string s_cfg_value = cfg["value"];
85  const std::string s_cfg_value_2nd = cfg["value_2nd"];
86 
87  for(const auto& branch : branches) {
88  const std::string s_branch_hits = branch.attributes["hits"];
89  const std::string s_branch_direction = branch.attributes["direction"];
90  const std::string s_branch_terrain = branch.attributes["terrain_types"];
91  const std::string s_branch_value = branch.attributes["value"];
92  const std::string s_branch_value_2nd = branch.attributes["value_second"];
93 
94  if(!s_branch_hits.empty() && s_branch_hits == s_cfg_hits) {
95  previously_hits_set = true;
96  }
97 
98  if(!s_branch_direction.empty() && s_branch_direction == s_cfg_direction) {
99  previously_direction_set = true;
100  }
101 
102  if(!s_branch_terrain.empty() && s_branch_terrain == s_cfg_terrain) {
103  previously_terrain_set = true;
104  }
105 
106  if(!s_branch_value.empty() && s_branch_value == s_cfg_value) {
107  previously_value_set = true;
108  }
109 
110  if(!s_branch_value_2nd.empty() && s_branch_value_2nd == s_cfg_value_2nd) {
111  previously_value_2nd_set = true;
112  }
113  }
114 
115  // Merge all frames that have new matches and prune any impossible
116  // matches, e.g. hits='yes' and hits='no'
117  for(auto iter = branches.begin(); iter != branches.end(); /* nothing */) {
118  const std::string s_branch_hits = (*iter).attributes["hits"];
119  const std::string s_branch_direction = (*iter).attributes["direction"];
120  const std::string s_branch_terrain = (*iter).attributes["terrain_types"];
121  const std::string s_branch_value = (*iter).attributes["value"];
122  const std::string s_branch_value_2nd = (*iter).attributes["value_second"];
123 
124  const bool hits_match = (previously_hits_set && s_branch_hits != s_cfg_hits);
125  const bool direction_match = (previously_direction_set && s_branch_direction != s_cfg_direction);
126  const bool terrain_match = (previously_terrain_set && s_branch_terrain != s_cfg_terrain);
127  const bool value_match = (previously_value_set && s_branch_value != s_cfg_value);
128  const bool value_2nd_match = (previously_value_2nd_set && s_branch_value_2nd != s_cfg_value_2nd);
129 
130  if((!previously_hits_set || hits_match) &&
131  (!previously_direction_set || direction_match) &&
132  (!previously_terrain_set || terrain_match) &&
133  (!previously_value_set || value_match) &&
134  (!previously_value_2nd_set || value_2nd_match) &&
135  (hits_match || direction_match || terrain_match || value_match || value_2nd_match))
136  {
137  branches.erase(iter++);
138  } else {
139  (*iter).attributes.merge_attributes(cfg);
140  ++iter;
141  }
142  }
143 
144  // Then we prune all parent branches with similar matches as they
145  // now will not have the full frame list
146  for(auto iter = parent->branches.begin(); iter != parent->branches.end(); /* nothing */) {
147  const std::string s_branch_hits = (*iter).attributes["hits"];
148  const std::string s_branch_direction = (*iter).attributes["direction"];
149  const std::string s_branch_terrain = (*iter).attributes["terrain_types"];
150  const std::string s_branch_value = (*iter).attributes["value"];
151  const std::string s_branch_value_2nd = (*iter).attributes["value_second"];
152 
153  const bool hits_match = (previously_hits_set && s_branch_hits == s_cfg_hits);
154  const bool direction_match = (previously_direction_set && s_branch_direction == s_cfg_direction);
155  const bool terrain_match = (previously_terrain_set && s_branch_terrain == s_cfg_terrain);
156  const bool value_match = (previously_value_set && s_branch_value == s_cfg_value);
157  const bool value_2nd_match = (previously_value_2nd_set && s_branch_value_2nd == s_cfg_value_2nd);
158 
159  if((!previously_hits_set || hits_match) &&
160  (!previously_direction_set || direction_match) &&
161  (!previously_terrain_set || terrain_match) &&
162  (!previously_value_set || value_match) &&
163  (!previously_value_2nd_set || value_2nd_match) &&
164  (hits_match || direction_match || terrain_match || value_match || value_2nd_match))
165  {
166  parent->branches.erase(iter++);
167  } else {
168  ++iter;
169  }
170  }
171  }
172 
174 
177 };
178 
179 static void prepare_single_animation(const config& anim_cfg, animation_branches& expanded_anims)
180 {
181  /* The anim_cursor holds the current parsing through the config and the branches hold the data
182  * that will be interpreted as the actual animation. The branches store the config attributes
183  * for each block and the children of those branches make up all the 'frame', 'missile_frame',
184  * etc. individually (so 2 instances of 'frame' would be stored as 2 children).
185  */
186  std::list<animation_cursor> anim_cursors;
187  anim_cursors.emplace_back(anim_cfg);
188 
189  while(!anim_cursors.empty()) {
190  animation_cursor& ac = anim_cursors.back();
191 
192  // Reached end of sub-tag config block
193  if(ac.itors.empty()) {
194  if(!ac.parent) break;
195 
196  // Merge all the current branches into the parent.
197  ac.parent->branches.splice(ac.parent->branches.end(), ac.branches);
198  anim_cursors.pop_back();
199  continue;
200  }
201 
202  if(ac.itors.front().key != "if") {
203  // Append current config object to all the branches in scope.
204  for(animation_branch &ab : ac.branches) {
205  ab.children.push_back(ac.itors.begin());
206  }
207 
208  ac.itors.pop_front();
209  continue;
210  }
211 
212  int count = 0;
213  do {
214  // Copies the current branches to each cursor created for the conditional clauses.
215  // Merge attributes of the clause into them.
216  anim_cursors.emplace_back(ac.itors.front().cfg, &ac);
217  ac.itors.pop_front();
218  ++count;
219  } while (!ac.itors.empty() && ac.itors.front().key == "else");
220 
221  if(count > 1) {
222  // When else statements present, clear all branches before 'if'
223  ac.branches.clear();
224  }
225  }
226 
227 #if 0
228  // Debug aid
229  for(animation_branch& ab : anim_cursors.back().branches) {
230  std::cout << "--branch--\n" << ab.attributes;
231  for(config::all_children_iterator &ci : ab.children) {
232  std::cout << "--branchcfg--\n" << ci->cfg;
233  }
234  std::cout << "\n";
235  }
236 #endif
237 
238  // Create the config object describing each branch.
239  assert(anim_cursors.size() == 1);
240  animation_cursor& ac = anim_cursors.back();
241  expanded_anims.splice(expanded_anims.end(), ac.branches, ac.branches.begin(), ac.branches.end());
242 }
243 
244 static animation_branches prepare_animation(const config& cfg, const std::string& animation_tag)
245 {
246  animation_branches expanded_animations;
247  for(const config &anim : cfg.child_range(animation_tag)) {
248  prepare_single_animation(anim, expanded_animations);
249  }
250 
251  return expanded_animations;
252 }
253 
255  const unit_frame& frame, const std::string& event, const int variation, const frame_builder& builder)
256  : terrain_types_()
257  , unit_filter_()
258  , secondary_unit_filter_()
259  , directions_()
260  , frequency_(0)
261  , base_score_(variation)
262  , event_(utils::split(event))
263  , value_()
264  , primary_attack_filter_()
265  , secondary_attack_filter_()
266  , hits_()
267  , value2_()
268  , sub_anims_()
269  , unit_anim_(start_time,builder)
270  , src_()
271  , dst_()
272  , invalidated_(false)
273  , play_offscreen_(true)
274  , overlaped_hex_()
275 {
276  add_frame(frame.duration(),frame,!frame.does_not_change());
277 }
278 
279 unit_animation::unit_animation(const config& cfg,const std::string& frame_string )
280  : terrain_types_(t_translation::read_list(cfg["terrain_type"].str()))
281  , unit_filter_()
283  , directions_()
284  , frequency_(cfg["frequency"])
285  , base_score_(cfg["base_score"])
286  , event_()
287  , value_()
290  , hits_()
291  , value2_()
292  , sub_anims_()
293  , unit_anim_(cfg,frame_string)
294  , src_()
295  , dst_()
296  , invalidated_(false)
297  , play_offscreen_(true)
298  , overlaped_hex_()
299 {
300  //if(!cfg["debug"].empty()) printf("DEBUG WML: FINAL\n%s\n\n",cfg.debug().c_str());
301 
302  for(const config::any_child fr : cfg.all_children_range()) {
303  if(fr.key == frame_string) {
304  continue;
305  }
306 
307  if(fr.key.find("_frame", fr.key.size() - 6) == std::string::npos) {
308  continue;
309  }
310 
311  if(sub_anims_.find(fr.key) != sub_anims_.end()) {
312  continue;
313  }
314 
315  sub_anims_[fr.key] = particle(cfg, fr.key.substr(0, fr.key.size() - 5));
316  }
317 
318  event_ = utils::split(cfg["apply_to"]);
319 
320  const std::vector<std::string>& my_directions = utils::split(cfg["direction"]);
321  for(const auto& direction : my_directions) {
323  directions_.push_back(d);
324  }
325 
326  /*const filter_context* fc = game_display::get_singleton();
327  if(!fc) {
328  // This is a pointer to the gamestate. Would prefer to tie unit animations only to the display, but for now this
329  // is an acceptable fallback. It seems to be relevant because when a second game is created, it seems that the
330  // game_display is null at the time that units are being constructed, and hence at the time that this code is running.
331  // A different solution might be to delay the team_builder stage 2 call until after the gui is initialized. Note that
332  // the current set up could conceivably cause problems with the editor, iirc it doesn't initialize a filter context.
333  fc = resources::filter_con;
334  assert(fc);
335  }*/
336 
337  for(const config& filter : cfg.child_range("filter")) {
338  unit_filter_.push_back(filter);
339  }
340 
341  for(const config& filter : cfg.child_range("filter_second")) {
342  secondary_unit_filter_.push_back(filter);
343  }
344 
345  for(const auto& v : utils::split(cfg["value"])) {
346  value_.push_back(atoi(v.c_str()));
347  }
348 
349  for(const auto& h : utils::split(cfg["hits"])) {
350  if(h == "yes" || h == "hit") {
351  hits_.push_back(hit_type::HIT);
352  }
353 
354  if(h == "no" || h == "miss") {
355  hits_.push_back(hit_type::MISS);
356  }
357 
358  if(h == "yes" || h == "kill" ) {
359  hits_.push_back(hit_type::KILL);
360  }
361  }
362 
363  for(const auto& v2 : utils::split(cfg["value_second"])) {
364  value2_.push_back(atoi(v2.c_str()));
365  }
366 
367  for(const config& filter : cfg.child_range("filter_attack")) {
368  primary_attack_filter_.push_back(filter);
369  }
370 
371  for(const config& filter : cfg.child_range("filter_second_attack")) {
372  secondary_attack_filter_.push_back(filter);
373  }
374 
375  play_offscreen_ = cfg["offscreen"].to_bool(true);
376 }
377 
378 int unit_animation::matches(const display& disp, const map_location& loc, const map_location& second_loc,
379  unit_const_ptr my_unit, const std::string& event, const int value, hit_type hit, const_attack_ptr attack,
380  const_attack_ptr second_attack, int value2) const
381 {
382  int result = base_score_;
383 
384  if(!event.empty() && !event_.empty()) {
385  if(std::find(event_.begin(), event_.end(), event) == event_.end()) {
386  return MATCH_FAIL;
387  }
388 
389  result++;
390  }
391 
392  if(!terrain_types_.empty()) {
394  return MATCH_FAIL;
395  }
396 
397  result++;
398  }
399 
400  if(!value_.empty()) {
401  if(std::find(value_.begin(), value_.end(), value) == value_.end()) {
402  return MATCH_FAIL;
403  }
404 
405  result++;
406  }
407 
408  if(my_unit) {
409  if(!directions_.empty()) {
410  if(std::find(directions_.begin(), directions_.end(), my_unit->facing()) == directions_.end()) {
411  return MATCH_FAIL;
412  }
413 
414  result++;
415  }
416 
417  for(const auto& filter : unit_filter_) {
418  unit_filter f{ vconfig(filter) };
419  if(!f(*my_unit, loc)) return MATCH_FAIL;
420  ++result;
421  }
422 
423  if(!secondary_unit_filter_.empty()) {
424  unit_map::const_iterator unit = disp.get_units().find(second_loc);
425  if(!unit.valid()) {
426  return MATCH_FAIL;
427  }
428 
429  for(const config& c : secondary_unit_filter_) {
430  unit_filter f{ vconfig(c) };
431  if(!f(*unit, second_loc)) return MATCH_FAIL;
432  result++;
433  }
434  }
435  } else if(!unit_filter_.empty()) {
436  return MATCH_FAIL;
437  }
438 
439  if(frequency_ && !(randomness::rng::default_instance().get_random_int(0, frequency_-1))) {
440  return MATCH_FAIL;
441  }
442 
443  if(!hits_.empty()) {
444  if(std::find(hits_.begin(),hits_.end(),hit) == hits_.end()) {
445  return MATCH_FAIL;
446  }
447 
448  result ++;
449  }
450 
451  if(!value2_.empty()) {
452  if(std::find(value2_.begin(),value2_.end(),value2) == value2_.end()) {
453  return MATCH_FAIL;
454  }
455 
456  result ++;
457  }
458 
459  if(!attack) {
460  if(!primary_attack_filter_.empty()) {
461  return MATCH_FAIL;
462  }
463  }
464 
465  for(const auto& iter : primary_attack_filter_) {
466  if(!attack->matches_filter(iter)) return MATCH_FAIL;
467  result++;
468  }
469 
470  if(!second_attack) {
471  if(!secondary_attack_filter_.empty()) {
472  return MATCH_FAIL;
473  }
474  }
475 
476  for(const auto& iter : secondary_attack_filter_) {
477  if(!second_attack->matches_filter(iter)) return MATCH_FAIL;
478  result++;
479  }
480 
481  return result;
482 }
483 
484 void unit_animation::fill_initial_animations(std::vector<unit_animation>& animations, const config& cfg)
485 {
486  add_anims(animations, cfg);
487 
488  std::vector<unit_animation> animation_base;
489  for(const auto& anim : animations) {
490  if(std::find(anim.event_.begin(), anim.event_.end(), "default") != anim.event_.end()) {
491  animation_base.push_back(anim);
492  animation_base.back().base_score_ += unit_animation::DEFAULT_ANIM;
493  animation_base.back().event_.clear();
494  }
495  }
496 
497  const std::string default_image = cfg["image"];
498 
499  if(animation_base.empty()) {
500  animation_base.push_back(unit_animation(0, frame_builder().image(default_image).duration(1), "", unit_animation::DEFAULT_ANIM));
501  }
502 
503  animations.push_back(unit_animation(0, frame_builder().image(default_image).duration(1), "_disabled_", 0));
504  animations.push_back(unit_animation(0,
505  frame_builder().image(default_image).duration(300).blend("0.0~0.3:100,0.3~0.0:200", {255,255,255}),
506  "_disabled_selected_", 0));
507 
508  for(const auto& base : animation_base) {
509  animations.push_back(base);
510  animations.back().event_ = { "standing" };
511  animations.back().play_offscreen_ = false;
512 
513  animations.push_back(base);
514  animations.back().event_ = { "_ghosted_" };
515  animations.back().unit_anim_.override(0, animations.back().unit_anim_.get_animation_duration(),particle::UNSET,"0.9", "", {0,0,0}, "", "", "~GS()");
516 
517  animations.push_back(base);
518  animations.back().event_ = { "_disabled_ghosted_" };
519  animations.back().unit_anim_.override(0, 1, particle::UNSET, "0.4", "", {0,0,0}, "", "", "~GS()");
520 
521  animations.push_back(base);
522  animations.back().event_ = { "selected" };
523  animations.back().unit_anim_.override(0, 300, particle::UNSET, "", "0.0~0.3:100,0.3~0.0:200", {255,255,255});
524 
525  animations.push_back(base);
526  animations.back().event_ = { "recruited" };
527  animations.back().unit_anim_.override(0, 600, particle::NO_CYCLE, "0~1:600");
528 
529  animations.push_back(base);
530  animations.back().event_ = { "levelin" };
531  animations.back().unit_anim_.override(0, 600, particle::NO_CYCLE, "", "1~0:600", {255,255,255});
532 
533  animations.push_back(base);
534  animations.back().event_ = { "levelout" };
535  animations.back().unit_anim_.override(0, 600, particle::NO_CYCLE, "", "0~1:600,1", {255,255,255});
536 
537  animations.push_back(base);
538  animations.back().event_ = { "pre_movement" };
539  animations.back().unit_anim_.override(0, 1, particle::NO_CYCLE);
540 
541  animations.push_back(base);
542  animations.back().event_ = { "post_movement" };
543  animations.back().unit_anim_.override(0, 1, particle::NO_CYCLE);
544 
545  animations.push_back(base);
546  animations.back().event_ = { "movement" };
547  animations.back().unit_anim_.override(0, 200,
548  particle::NO_CYCLE, "", "", {0,0,0}, "0~1:200", std::to_string(display::LAYER_UNIT_MOVE_DEFAULT - display::LAYER_UNIT_FIRST));
549 
550  animations.push_back(base);
551  animations.back().event_ = { "defend" };
552  animations.back().unit_anim_.override(0, animations.back().unit_anim_.get_animation_duration(),
553  particle::NO_CYCLE, "", "0.0,0.5:75,0.0:75,0.5:75,0.0", {255,0,0});
554  animations.back().hits_.push_back(hit_type::HIT);
555  animations.back().hits_.push_back(hit_type::KILL);
556 
557  animations.push_back(base);
558  animations.back().event_ = { "defend" };
559 
560  animations.push_back(base);
561  animations.back().event_ = { "attack" };
562  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));
563  animations.back().primary_attack_filter_.emplace_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_.emplace_back("range", "ranged");
569 
570  animations.push_back(base);
571  animations.back().event_ = { "death" };
572  animations.back().unit_anim_.override(0, 600, particle::NO_CYCLE, "1~0:600");
573  animations.back().sub_anims_["_death_sound"] = particle();
574  animations.back().sub_anims_["_death_sound"].add_frame(1, frame_builder().sound(cfg["die_sound"]), true);
575 
576  animations.push_back(base);
577  animations.back().event_ = { "victory" };
578  animations.back().unit_anim_.override(0, animations.back().unit_anim_.get_animation_duration(), particle::CYCLE);
579 
580  animations.push_back(base);
581  animations.back().unit_anim_.override(0, 150, particle::NO_CYCLE, "1~0:150");
582  animations.back().event_ = { "pre_teleport" };
583 
584  animations.push_back(base);
585  animations.back().unit_anim_.override(0, 150, particle::NO_CYCLE, "0~1:150,1");
586  animations.back().event_ = { "post_teleport" };
587 
588  animations.push_back(base);
589  animations.back().event_ = { "healing" };
590 
591  animations.push_back(base);
592  animations.back().event_ = { "healed" };
593  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});
594 
595  const std::string healed_sound = get_heal_sound(cfg);
596 
597  animations.back().sub_anims_["_healed_sound"].add_frame(1, frame_builder().sound(healed_sound), true);
598 
599  animations.push_back(base);
600  animations.back().event_ = { "poisoned" };
601  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});
602  animations.back().sub_anims_["_poison_sound"] = particle();
603  animations.back().sub_anims_["_poison_sound"].add_frame(1, frame_builder().sound(game_config::sounds::status::poisoned), true);
604  }
605 }
606 
607 static void add_simple_anim(std::vector<unit_animation>& animations,
608  const config& cfg, char const* tag_name, char const* apply_to,
610  bool offscreen = true)
611 {
612  for(const animation_branch& ab : prepare_animation(cfg, tag_name)) {
613  config anim = ab.merge();
614  anim["apply_to"] = apply_to;
615 
616  if(!offscreen) {
617  config::attribute_value& v = anim["offscreen"];
618  if(v.empty()) v = false;
619  }
620 
621  config::attribute_value& v = anim["layer"];
622  if(v.empty()) v = layer - display::LAYER_UNIT_FIRST;
623 
624  animations.emplace_back(anim);
625  }
626 }
627 
628 void unit_animation::add_anims( std::vector<unit_animation> & animations, const config & cfg)
629 {
630  for(const animation_branch& ab : prepare_animation(cfg, "animation")) {
631  animations.emplace_back(ab.merge());
632  }
633 
634  const int default_layer = display::LAYER_UNIT_DEFAULT - display::LAYER_UNIT_FIRST;
637 
638  add_simple_anim(animations, cfg, "resistance_anim", "resistance");
639  add_simple_anim(animations, cfg, "leading_anim", "leading");
640  add_simple_anim(animations, cfg, "teaching_anim", "teaching");
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  std::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.emplace_back(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  std::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.emplace_back(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.emplace_back(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.emplace_back(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.emplace_back(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.emplace_back(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.emplace_back(anim);
773  animations.back().base_score_--; //so default doesn't interfere with 'if' block
774 
775  anim["hits"] = true;
776  animations.emplace_back(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.emplace_back(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.emplace_back(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.emplace_back(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.emplace_back(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.emplace_back(anim);
880  animations.back().unit_anim_.set_end_time(0);
881 
882  anim["apply_to"] ="post_teleport";
883  animations.emplace_back(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) {
908  add_frame(duration -get_animation_duration(), get_last_frame());
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 {
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 
1150 std::string unit_animation::debug() const
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(std::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(std::size_t i = 0; i < p.second.get_frames_count(); i++) {
1228  std::string sub_frame_name = p.first;
1229  std::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.
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();
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();
1294 }
1295 
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 = std::move(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 = tmp.my_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 
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 = std::move(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  , const unit_animation::hit_type hit_type
1356  , const_attack_ptr attack
1357  , const_attack_ptr second_attack
1358  , int value2) const
1359 {
1360  display* disp = display::get_singleton();
1361  return (animated_unit && animated_unit->anim_comp().choose_animation(*disp, src, event, dst, value, hit_type, attack, second_attack, value2));
1362 }
1363 
1365  , const std::string& event
1366  , const map_location &src
1367  , const map_location & dst
1368  , const int value
1369  , bool with_bars
1370  , const std::string& text
1371  , const color_t text_color
1372  , const unit_animation::hit_type hit_type
1373  , const_attack_ptr attack
1374  , const_attack_ptr second_attack
1375  , int value2)
1376 {
1377  if(!animated_unit) return;
1378 
1379  display* disp = display::get_singleton();
1380  if(animated_unit->anim_comp().get_animation() &&
1381  !animated_unit->anim_comp().get_animation()->animation_finished_potential() &&
1382  animated_unit->anim_comp().get_animation()->matches(
1383  *disp, src, dst, animated_unit, event, value, hit_type, attack, second_attack, value2) > unit_animation::MATCH_FAIL)
1384  {
1385  anim_elem tmp;
1386  tmp.my_unit = animated_unit;
1387  tmp.text = text;
1388  tmp.text_color = text_color;
1389  tmp.src = src;
1390  tmp.with_bars= with_bars;
1391  tmp.animation = nullptr;
1392 
1393  animated_units_.push_back(std::move(tmp));
1394  } else {
1395  add_animation(animated_unit,event,src,dst,value,with_bars,text,text_color,hit_type,attack,second_attack,value2);
1396  }
1397 }
1398 
1400 {
1401  int begin_time = INT_MAX;
1402 
1403  for(const auto& anim : animated_units_) {
1404  if(anim.my_unit->anim_comp().get_animation()) {
1405  if(anim.animation) {
1406  begin_time = std::min<int>(begin_time, anim.animation->get_begin_time());
1407  } else {
1408  begin_time = std::min<int>(begin_time, anim.my_unit->anim_comp().get_animation()->get_begin_time());
1409  }
1410  }
1411  }
1412 
1413  for(auto& anim : animated_units_) {
1414  if(anim.animation) {
1415  anim.my_unit->anim_comp().start_animation(begin_time, anim.animation, anim.with_bars, anim.text, anim.text_color);
1416  anim.animation = nullptr;
1417  } else {
1418  anim.my_unit->anim_comp().get_animation()->update_parameters(anim.src, anim.src.get_direction(anim.my_unit->facing()));
1419  }
1420  }
1421 }
1422 
1424 {
1425  bool finished = true;
1426  for(const auto& anim : animated_units_) {
1427  finished &= anim.my_unit->anim_comp().get_animation()->animation_finished_potential();
1428  }
1429 
1430  return finished;
1431 }
1432 
1433 void unit_animator::wait_until(int animation_time) const
1434 {
1435  // important to set a max animation time so that the time does not go past this value for movements.
1436  // fix for bug #1565
1437  animated_units_[0].my_unit->anim_comp().get_animation()->set_max_animation_time(animation_time);
1438 
1439  display* disp = display::get_singleton();
1440  double speed = disp->turbo_speed();
1441 
1443 
1444  int end_tick = animated_units_[0].my_unit->anim_comp().get_animation()->time_to_tick(animation_time);
1445  while(SDL_GetTicks() < static_cast<unsigned int>(end_tick) - std::min<int>(static_cast<unsigned int>(20 / speed), 20)) {
1446  CVideo::delay(std::max<int>(0, std::min<int>(10, static_cast<int>((animation_time - get_animation_time()) * speed))));
1447 
1449  end_tick = animated_units_[0].my_unit->anim_comp().get_animation()->time_to_tick(animation_time);
1450  }
1451 
1452  CVideo::delay(std::max<int>(0, end_tick - SDL_GetTicks() + 5));
1453 
1455  animated_units_[0].my_unit->anim_comp().get_animation()->set_max_animation_time(0);
1456 }
1457 
1459 {
1460  if(game_config::no_delay) return;
1461 
1462  bool finished = false;
1463  while(!finished) {
1465 
1466  CVideo::delay(10);
1467 
1468  finished = true;
1469  for(const auto& anim : animated_units_) {
1470  finished &= anim.my_unit->anim_comp().get_animation()->animation_finished_potential();
1471  }
1472  }
1473 }
1474 
1476 {
1477  return animated_units_[0].my_unit->anim_comp().get_animation()->get_animation_time() ;
1478 }
1479 
1481 {
1482  return animated_units_[0].my_unit->anim_comp().get_animation()->get_animation_time_potential() ;
1483 }
1484 
1486 {
1487  int end_time = INT_MIN;
1488  for(const auto& anim : animated_units_) {
1489  if(anim.my_unit->anim_comp().get_animation()) {
1490  end_time = std::max<int>(end_time, anim.my_unit->anim_comp().get_animation()->get_end_time());
1491  }
1492  }
1493 
1494  return end_time;
1495 }
1496 
1498 {
1499  for(const auto& anim : animated_units_) {
1500  if(anim.my_unit->anim_comp().get_animation()) {
1501  anim.my_unit->anim_comp().get_animation()->pause_animation();
1502  }
1503  }
1504 }
1505 
1507 {
1508  for(const auto& anim : animated_units_) {
1509  if(anim.my_unit->anim_comp().get_animation()) {
1510  anim.my_unit->anim_comp().get_animation()->restart_animation();
1511  }
1512  }
1513 }
1514 
1516 {
1517  for(const auto& anim : animated_units_) {
1518  anim.my_unit->anim_comp().set_standing();
1519  }
1520 }
const frame_parameters parameters(int current_time) const
Getters for the different parameters.
Definition: frame.cpp:291
play_controller * controller
Definition: resources.cpp:22
void wait_for_end() const
Definition: animation.cpp:1458
const std::string & get_modifications() const
Definition: picture.hpp:83
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:201
Reserve layers to be selected for WML.
Definition: display.hpp:833
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:624
std::size_t get_frames_count() const
static DIRECTION parse_direction(const std::string &str)
Definition: location.cpp:66
All parameters from a frame at a given instant.
Definition: frame.hpp:35
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:92
unit_animation()=delete
std::vector< config::const_all_children_iterator > children
Definition: animation.cpp:55
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:978
static std::string get_heal_sound(const config &cfg)
Definition: animation.cpp:32
animation_cursor * parent
Definition: animation.cpp:176
void start_animations()
Definition: animation.cpp:1399
bool invalidate(const map_location &loc)
Function to invalidate a specific tile for redrawing.
Definition: display.cpp:3026
std::vector< hit_type > hits_
Definition: animation.hpp:184
This class represents a single unit of a specific type.
Definition: unit.hpp:121
Keep most parameters in a separate class to simplify the handling of the large number of parameters b...
Definition: frame.hpp:142
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:719
Variant for storing WML attributes.
std::vector< map_location::DIRECTION > directions_
Definition: animation.hpp:177
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:168
bool would_end() const
Definition: animation.cpp:1423
animation_cursor(const config &cfg)
Definition: animation.cpp:62
static void add_anims(std::vector< unit_animation > &animations, const config &cfg)
Definition: animation.cpp:628
std::vector< config > secondary_attack_filter_
Definition: animation.hpp:183
static void prepare_single_animation(const config &anim_cfg, animation_branches &expanded_anims)
Definition: animation.cpp:179
child_itors child_range(config_key_type key)
Definition: config.cpp:344
int get_current_frame_begin_time() const
Definition: animation.hpp:102
bool animation_finished_potential() const
void add_frame(int duration, const unit_frame &value, bool force_change=false)
Definition: animation.hpp:54
void clear_haloes()
Definition: animation.cpp:1097
const unit_animation * animation
Definition: animation.hpp:293
std::vector< int > value_
Definition: animation.hpp:181
const unit_map & get_units() const
Definition: display.hpp:125
virtual void play_slice(bool is_delay_enabled=true)
int get_begin_time() const
Definition: animation.cpp:1023
#define h
particle unit_anim_
Definition: animation.hpp:187
Audio output for sound and music.
Definition: sound.cpp:41
#define d
bool need_update() const
Definition: animation.cpp:959
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:321
bool need_update() const
Definition: animation.cpp:914
drawing_layer
The layers to render something on.
Definition: display.hpp:824
int get_end_time() const
Definition: animation.cpp:1013
bool need_minimal_update() const
Definition: animation.cpp:922
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:238
map_location dst_
Definition: animation.hpp:190
int get_animation_time_potential() const
Definition: animation.cpp:1480
void pause_animation()
Definition: animation.cpp:1065
void wait_until(int animation_time) const
Definition: animation.cpp:1433
int get_animation_duration() const
std::shared_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:27
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:133
void start_animation(int start_time)
Definition: animation.cpp:1288
double turbo_speed() const
Definition: display.cpp:2400
void new_animation_frame()
Definition: animated.cpp:31
void restart_animation()
Definition: animation.cpp:1506
const T & get_frame(std::size_t n) const
terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition: map.cpp:302
config & add_child_at(config_key_type key, const config &val, unsigned index)
Definition: config.cpp:546
std::set< map_location > overlaped_hex_
Definition: animation.hpp:194
bool does_not_change() const
Definition: frame.hpp:228
unit_const_ptr my_unit
Definition: animation.hpp:292
std::vector< config > secondary_unit_filter_
Definition: animation.hpp:176
std::vector< std::string > event_
Definition: animation.hpp:180
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:1247
all_children_iterator erase(const all_children_iterator &i)
Definition: config.cpp:727
const unit_frame & get_last_frame() const
Definition: animation.hpp:49
Generic locator abstracting the location of an image.
Definition: picture.hpp:60
void restart_animation()
Definition: animation.cpp:1074
Encapsulates the map of the game.
Definition: location.hpp:38
boost::iterator_range< const_child_iterator > const_child_itors
Definition: config.hpp:206
void add_animation(unit_const_ptr 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
void restart_animation()
Definition: animated.hpp:56
unit_iterator find(std::size_t id)
Definition: map.cpp:310
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:476
std::size_t i
Definition: function.cpp:967
animation_branches branches
Definition: animation.cpp:175
int get_end_time() const
Definition: animation.cpp:1485
const unit_frame & get_current_frame() const
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:607
default layer for drawing moving units
Definition: display.hpp:844
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:58
mock_party p
void update_last_draw_time(double acceleration=0)
default layer for drawing units
Definition: display.hpp:835
void pause_animation()
Definition: animation.cpp:1497
void replace_anim_if_invalid(unit_const_ptr 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:1364
map_location src_
Definition: animation.hpp:189
default layer for missile frames
Definition: display.hpp:849
void start_animation(int start_time, bool cycles=false)
Starts an animation cycle.
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:2114
std::vector< int > value2_
Definition: animation.hpp:185
config::const_all_children_itors itors
Definition: animation.cpp:173
DIRECTION
Valid directions which can be moved in our hexagonal world.
Definition: location.hpp:40
config merge() const
Definition: animation.cpp:44
static void fill_initial_animations(std::vector< unit_animation > &animations, const config &cfg)
Definition: animation.cpp:484
const std::string & get_filename() const
Definition: picture.hpp:78
std::vector< config > unit_filter_
Definition: animation.hpp:175
config & add_child(config_key_type key)
Definition: config.cpp:514
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:182
int get_animation_time() const
Definition: animation.cpp:1475
int duration() const
Definition: frame.hpp:223
const gamemap & get_map() const
Definition: display.hpp:96
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:186
std::vector< std::string > split(const config_attribute_value &val)
std::string debug() const
Definition: animation.cpp:1150
void set_all_standing()
Definition: animation.cpp:1515
bool has_animation(unit_const_ptr 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, 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) const
has_animation : return an boolean value if animated unit present and have animation specified...
Definition: animation.cpp:1350
A variable-expanding proxy for the config class.
Definition: variable.hpp:44
void pause_animation()
Definition: animated.hpp:51
Functions to load and save images from/to disk.
static animation_branches prepare_animation(const config &cfg, const std::string &animation_tag)
Definition: animation.cpp:244
static void delay(unsigned int milliseconds)
Waits a given number of milliseconds before returning.
Definition: video.cpp:301
void redraw(frame_parameters &value, halo::manager &halo_man)
Definition: animation.cpp:1083
void update_last_draw_time()
Definition: animation.cpp:1004
bool need_minimal_update() const
Definition: animation.cpp:969
int get_end_time() const
bool animation_finished_potential() const
Definition: animation.cpp:994
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
int matches(const display &disp, const map_location &loc, const map_location &second_loc, unit_const_ptr 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:378
bool valid() const
Definition: map.hpp:274
mock_char c
std::shared_ptr< const attack_type > const_attack_ptr
Definition: ptr.hpp:34
bool play_offscreen_
Definition: animation.hpp:193
t_translation::ter_list terrain_types_
Definition: animation.hpp:174
static std::string write_direction(DIRECTION dir)
Definition: location.cpp:141
animation_cursor(const config &cfg, animation_cursor *p)
Definition: animation.cpp:68
boost::tribool primary_frame
Definition: frame.hpp:69
static rng & default_instance()
Definition: random.cpp:74
bool does_not_change() const
Definition: frame.cpp:267
bool empty() const
Definition: config.cpp:941
bool animation_finished() const
Returns true if the current animation was finished.
std::string debug() const
Definition: config.cpp:1347
ter_list read_list(std::string_view str, const ter_layer filler)
Reads a list of terrains from a string, when reading the.
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
bool animation_finished() const
Definition: animation.cpp:984
int get_animation_time() const