The Battle for Wesnoth  1.15.0-dev
udisplay.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
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 /** @file */
16 
17 #include "units/udisplay.hpp"
18 
19 #include "color.hpp"
20 #include "fake_unit_manager.hpp"
21 #include "fake_unit_ptr.hpp"
22 #include "game_board.hpp"
23 #include "game_display.hpp"
24 #include "log.hpp"
25 #include "mouse_events.hpp"
26 #include "preferences/game.hpp"
27 #include "resources.hpp"
28 #include "sound.hpp"
29 #include "terrain/filter.hpp"
31 #include "units/filter.hpp"
32 #include "units/map.hpp"
33 #include "units/unit.hpp"
34 
35 #define LOG_DP LOG_STREAM(info, display)
36 
37 namespace unit_display
38 {
39 namespace
40 {
41 /**
42  * Returns a string whose first line is @a number, centered over a second line,
43  * which consists of @a text.
44  * If the number is 0, the first line is suppressed.
45  */
46 std::string number_and_text(int number, const std::string& text)
47 {
48  // Simple case.
49  if(number == 0) {
50  return text;
51  }
52 
53  std::ostringstream result;
54 
55  if(text.empty()) {
56  result << number;
57  } else {
58  result << std::string((text.size() + 1) / 2, ' ') << number << '\n' << text;
59  }
60 
61  return result.str();
62 }
63 
64 /**
65  * Animates a teleportation between hexes.
66  *
67  * @param a The starting hex.
68  * @param b The ending hex.
69  * @param temp_unit The unit to animate (historically, a temporary unit).
70  * @param disp The game display. Assumed neither locked nor faked.
71  */
72 void teleport_unit_between(const map_location& a, const map_location& b, unit& temp_unit, display& disp)
73 {
74  if(disp.fogged(a) && disp.fogged(b)) {
75  return;
76  }
77 
78  temp_unit.set_location(a);
79 
80  if(!disp.fogged(a)) { // teleport
81  temp_unit.set_facing(a.get_relative_dir(b));
82  disp.scroll_to_tiles(a, b, game_display::ONSCREEN, true, 0.0, false);
83  unit_animator animator;
84  animator.add_animation(&temp_unit, "pre_teleport", a);
85  animator.start_animations();
86  animator.wait_for_end();
87  }
88 
89  temp_unit.set_location(b);
90 
91  if(!disp.fogged(b)) { // teleport
92  temp_unit.set_facing(a.get_relative_dir(b));
93  disp.scroll_to_tiles(b, a, game_display::ONSCREEN, true, 0.0, false);
94  unit_animator animator;
95  animator.add_animation(&temp_unit, "post_teleport", b);
96  animator.start_animations();
97  animator.wait_for_end();
98  }
99 
100  temp_unit.anim_comp().set_standing();
101  disp.draw_debugging_aids();
102 
104 }
105 
106 /**
107  * Animates a single step between hexes.
108  * This will return before the animation actually finishes, allowing other
109  * processing to occur during the animation.
110  *
111  * @param a The starting hex.
112  * @param b The ending hex.
113  * @param temp_unit The unit to animate (historically, a temporary unit).
114  * @param step_num The number of steps taken so far (used to pick an animation).
115  * @param step_left The number of steps remaining (used to pick an animation).
116  * @param animator The unit_animator to use. This is assumed clear when we start,
117  * but will likely not be clear when we return.
118  * @param disp The game display. Assumed neither locked nor faked.
119  * @returns The animation potential until this animation will finish.
120  * INT_MIN indicates that no animation is pending.
121  */
122 int move_unit_between(const map_location& a,
123  const map_location& b,
124  unit_ptr temp_unit,
125  unsigned int step_num,
126  unsigned int step_left,
127  unit_animator& animator,
128  display& disp)
129 {
130  if(disp.fogged(a) && disp.fogged(b)) {
131  return INT_MIN;
132  }
133 
134  temp_unit->set_location(a);
135  temp_unit->set_facing(a.get_relative_dir(b));
136 
137  animator.replace_anim_if_invalid(
138  temp_unit.get(),
139  "movement",
140  a, b,
141  step_num,
142  false,
143  "",
144  {0, 0, 0},
145  unit_animation::hit_type::INVALID,
146  nullptr, nullptr,
147  step_left
148  );
149 
150  animator.start_animations();
151  animator.pause_animation();
152 
153  disp.scroll_to_tiles(a, b, game_display::ONSCREEN, true, 0.0, false);
154 
155  animator.restart_animation();
156 
157  // useless now, previous short draw() just did one
158  // new_animation_frame();
159 
160  int target_time = animator.get_animation_time_potential();
161 
162  // target_time must be short to avoid jumpy move
163  // std::cout << "target time: " << target_time << "\n";
164  // we round it to the next multiple of 200 so that movement aligns to hex changes properly
165  target_time += 200;
166  target_time -= target_time % 200;
167 
168  return target_time;
169 }
170 
171 bool do_not_show_anims(display* disp)
172 {
173  return !disp || disp->video().update_locked() || disp->video().faked();
174 }
175 
176 } // end anon namespace
177 
178 /**
179  * The path must remain unchanged for the life of this object.
180  */
181 unit_mover::unit_mover(const std::vector<map_location>& path, bool animate, bool force_scroll)
182  : disp_(game_display::get_singleton())
183  , can_draw_(disp_ && !disp_->video().update_locked() && !disp_->video().faked() && path.size() > 1)
184  , animate_(animate)
185  , force_scroll_(force_scroll)
186  , animator_()
187  , wait_until_(INT_MIN)
188  , shown_unit_()
189  , path_(path)
190  , current_(0)
191  , temp_unit_ptr_()
192  // Somewhat arbitrary default values.
193  , was_hidden_(false)
194  , is_enemy_(true)
195 {
196  // Some error conditions that indicate something has gone very wrong.
197  // (This class can handle these conditions, but someone wanted them
198  // to be assertions.)
199  assert(!path_.empty());
200  assert(disp_);
201 }
202 
204 {
205  // Make sure a unit hidden for movement is unhidden.
207 
208  // For safety, clear the animator before deleting the temp unit.
209  animator_.clear();
210 }
211 
212 /**
213  * Makes the temporary unit used by this match the supplied unit.
214  * This is called when setting the initial unit, as well as replacing it with
215  * something new.
216  * When this finishes, the supplied unit is hidden, while the temporary unit
217  * is not hidden.
218  */
219 /* Note: Hide the unit in its current location; do not actually remove it.
220  * Otherwise the status displays will be wrong during the movement.
221  */
223 {
224  // No point in creating a temp unit with no way to display it.
225  if(disp_ == nullptr) {
226  return;
227  }
228 
229  // Save the hidden state of the unit.
230  was_hidden_ = u->get_hidden();
231 
232  // Make our temporary unit mostly match u...
234 
235  // ... but keep the temporary unhidden and hide the original.
236  temp_unit_ptr_->set_hidden(false);
237  u->set_hidden(true);
238 
239  // Update cached data.
240  is_enemy_ = resources::gameboard->get_team(u->side()).is_enemy(disp_->viewing_side());
241 }
242 
243 /**
244  * Switches the display back to *shown_unit_ after animating.
245  * This uses temp_unit_ptr_, so (in the destructor) call this before deleting
246  * temp_unit_ptr_.
247  */
249 {
250  if(shown_unit_) {
251  // Switch the display back to the real unit.
252  shown_unit_->set_hidden(was_hidden_);
253  temp_unit_ptr_->set_hidden(true);
254  shown_unit_.reset();
255  }
256 }
257 
258 /**
259  * Initiates the display of movement for the supplied unit.
260  * This should be called before attempting to display moving to a new hex.
261  */
263 {
264  // Nothing to do here if there is nothing to animate.
265  if(!can_draw_) {
266  return;
267  }
268 
269  // If no animation then hide unit until end of movement
270  if(!animate_) {
271  was_hidden_ = u->get_hidden();
272  u->set_hidden(true);
273  return;
274  }
275 
276  // This normally does nothing, but just in case...
277  wait_for_anims();
278 
279  // Visually replace the original unit with the temporary.
280  // (Original unit is left on the map, so the unit count is correct.)
282 
283  // Initialize our temporary unit for the move.
284  temp_unit_ptr_->set_location(path_[0]);
285  temp_unit_ptr_->set_facing(path_[0].get_relative_dir(path_[1]));
286  temp_unit_ptr_->anim_comp().set_standing(false);
287 
288  // If the unit can be seen here by the viewing side:
289  if(!is_enemy_ || !temp_unit_ptr_->invisible(path_[0])) {
290  // Scroll to the path, but only if it fully fits on screen.
291  // If it does not fit we might be able to do a better scroll later.
292  disp_->scroll_to_tiles(path_, game_display::ONSCREEN, true, true, 0.0, false);
293  }
294 
295  // extra immobile movement animation for take-off
296  animator_.add_animation(temp_unit_ptr_.get(), "pre_movement", path_[0], path_[1]);
299  animator_.clear();
300 
301  // Switch the display back to the real unit.
302  u->set_facing(temp_unit_ptr_->facing());
303  u->anim_comp().set_standing(false); // Need to reset u's animation so the new facing takes effect.
304  u->set_hidden(was_hidden_);
305 
306  temp_unit_ptr_->set_hidden(true);
307 }
308 
309 /**
310  * Visually moves a unit from the last hex we drew to the one specified by
311  * @a path_index. If @a path_index points to an earlier hex, we do nothing.
312  * The moving unit will only be updated if update is set to true; otherwise,
313  * the provided unit is merely hidden during the movement and re-shown after.
314  * (Not updating the unit can produce smoother animations in some cases.)
315  * If @a wait is set to false, this returns without waiting for the final
316  * animation to finish. Call wait_for_anims() to explicitly get this final
317  * wait (another call to proceed_to() or finish() will implicitly wait). The
318  * unit must remain valid until the wait is finished.
319  */
320 void unit_mover::proceed_to(unit_ptr u, std::size_t path_index, bool update, bool wait)
321 {
322  // Nothing to do here if animations cannot be shown.
323  if(!can_draw_ || !animate_) {
324  return;
325  }
326 
327  // Handle pending visibility issues before introducing new ones.
328  wait_for_anims();
329 
330  if(update || !temp_unit_ptr_) {
331  // Replace the temp unit (which also hides u and shows our temporary).
333  } else {
334  // Just switch the display from the real unit to our fake one.
335  temp_unit_ptr_->set_hidden(false);
336  u->set_hidden(true);
337  }
338 
339  // Safety check.
340  path_index = std::min(path_index, path_.size() - 1);
341 
342  for(; current_ < path_index; ++current_)
343  // If the unit can be seen by the viewing side while making this step:
344  if(!is_enemy_
345  || !temp_unit_ptr_->invisible(path_[current_])
346  || !temp_unit_ptr_->invisible(path_[current_ + 1])
347  ) {
348  // Wait for the previous step to complete before drawing the next one.
349  wait_for_anims();
350 
351  if(!disp_->tile_fully_on_screen(path_[current_]) || !disp_->tile_fully_on_screen(path_[current_ + 1])) {
352  // prevent the unit from disappearing if we scroll here with i == 0
353  temp_unit_ptr_->set_location(path_[current_]);
354 
355  // scroll in as much of the remaining path as possible
356  if(temp_unit_ptr_->anim_comp().get_animation()) {
357  temp_unit_ptr_->anim_comp().get_animation()->pause_animation();
358  }
359 
361  path_.begin() + current_, path_.end(), game_display::ONSCREEN, true, false, 0.0, force_scroll_);
362 
363  if(temp_unit_ptr_->anim_comp().get_animation()) {
364  temp_unit_ptr_->anim_comp().get_animation()->restart_animation();
365  }
366  }
367 
368  if(tiles_adjacent(path_[current_], path_[current_ + 1])) {
369  wait_until_ = move_unit_between(
370  path_[current_],
371  path_[current_ + 1],
373  current_, path_.size() - (current_ + 2),
374  animator_,
375  *disp_
376  );
377  } else if(path_[current_] != path_[current_ + 1]) {
378  teleport_unit_between(path_[current_], path_[current_ + 1], *temp_unit_ptr_, *disp_);
379  }
380  }
381 
382  // Update the unit's facing.
383  u->set_facing(temp_unit_ptr_->facing());
384  u->anim_comp().set_standing(false); // Need to reset u's animation so the new facing takes effect.
385 
386  // Remember the unit to unhide when the animation finishes.
387  shown_unit_ = u;
388 
389  if(wait) {
390  wait_for_anims();
391  }
392 }
393 
394 /**
395  * Waits for the final animation of the most recent proceed_to() to finish.
396  * It is not necessary to call this unless you want to wait before the next
397  * call to proceed_to() or finish().
398  */
400 {
401  if(wait_until_ == INT_MAX) {
402  // Wait for end (not currently used, but still supported).
404  } else if(wait_until_ != INT_MIN) {
405  // Wait until the specified time (used for normal movement).
407  // debug code, see unit_frame::redraw()
408  // std::cout << " end\n";
409  }
410 
411  // Reset data.
412  wait_until_ = INT_MIN;
413  animator_.clear();
414 
416 }
417 
418 /**
419  * Finishes the display of movement for the supplied unit.
420  * If called before showing the unit reach the end of the path, it will be
421  * assumed that the movement ended early.
422  * If @a dir is not supplied, the final direction will be determined by (the
423  * last two traversed hexes of) the path.
424  */
426 {
427  // Nothing to do here if the display is not valid.
428  if(!can_draw_) {
429  // Make sure to reset the unit's animation to deal with a quirk in the
430  // action engine where it leaves it to us to reenable bars even if the
431  // display is initially locked.
432  u->anim_comp().set_standing(true);
433  return;
434  }
435 
436  const map_location& end_loc = path_[current_];
437  const map_location::DIRECTION final_dir = current_ == 0
438  ? path_[0].get_relative_dir(path_[1])
439  : path_[current_ - 1].get_relative_dir(end_loc);
440 
441  if(animate_) {
442  wait_for_anims(); // In case proceed_to() did not wait for the last animation.
443 
444  // Make sure the displayed unit is correct.
446  temp_unit_ptr_->set_location(end_loc);
447  temp_unit_ptr_->set_facing(final_dir);
448 
449  // Animation
450  animator_.add_animation(temp_unit_ptr_.get(), "post_movement", end_loc);
453  animator_.clear();
454 
455  // Switch the display back to the real unit.
456  u->set_hidden(was_hidden_);
457  temp_unit_ptr_->set_hidden(true);
458 
460  mousehandler->invalidate_reachmap();
461  }
462  } else {
463  // Show the unit at end of skipped animation
464  u->set_hidden(was_hidden_);
465  }
466 
467  // Facing gets set even when not animating.
468  u->set_facing(dir == map_location::NDIRECTIONS ? final_dir : dir);
469  u->anim_comp().set_standing(true); // Need to reset u's animation so the new facing takes effect.
470 }
471 
472 /**
473  * Display a unit moving along a given path.
474  *
475  * @param path The path to traverse.
476  * @param u The unit to show being moved. Its facing will be updated,
477  * but not its position.
478  * @param animate If set to false, only side-effects of move are applied
479  * (correct unit facing, path hexes redrawing).
480  * @param dir Unit will be set facing this direction after move.
481  * If nothing passed, direction will be set based on path.
482  */
483 /* Note: Hide the unit in its current location,
484  * but don't actually remove it until the move is done,
485  * so that while the unit is moving status etc.
486  * will still display the correct number of units.
487  */
489  const std::vector<map_location>& path, unit_ptr u, bool animate, map_location::DIRECTION dir, bool force_scroll)
490 {
491  unit_mover mover(path, animate, force_scroll);
492  mover.start(u);
493  mover.proceed_to(u, path.size());
494  mover.finish(u, dir);
495 }
496 
497 void reset_helpers(const unit* attacker, const unit* defender);
498 
500  unit& attacker,
501  const_attack_ptr attack,
502  const_attack_ptr secondary_attack,
503  const map_location& defender_loc,
504  unit* defender)
505 {
507  if(do_not_show_anims(disp) || disp->fogged(loc) || !preferences::show_combat()) {
508  return;
509  }
510 
511  unit_animator animator;
512 
513  attacker.set_facing(loc.get_relative_dir(defender_loc));
514  defender->set_facing(defender_loc.get_relative_dir(loc));
515 
516  animator.add_animation(&attacker, "draw_weapon", loc, defender_loc, 0, false, "", {0, 0, 0},
517  unit_animation::hit_type::HIT, attack, secondary_attack, 0);
518  animator.add_animation(defender, "draw_weapon", defender_loc, loc, 0, false, "", {0, 0, 0},
519  unit_animation::hit_type::MISS, secondary_attack, attack, 0);
520 
521  animator.start_animations();
522  animator.wait_for_end();
523 }
524 
525 void unit_sheath_weapon(const map_location& primary_loc,
526  unit* primary_unit,
527  const_attack_ptr primary_attack,
528  const_attack_ptr secondary_attack,
529  const map_location& secondary_loc,
530  unit* secondary_unit)
531 {
533  if(do_not_show_anims(disp) || disp->fogged(primary_loc) || !preferences::show_combat()) {
534  return;
535  }
536 
537  unit_animator animator;
538  if(primary_unit) {
539  animator.add_animation(primary_unit, "sheath_weapon", primary_loc, secondary_loc, 0, false, "", {0, 0, 0},
540  unit_animation::hit_type::INVALID, primary_attack, secondary_attack, 0);
541  }
542 
543  if(secondary_unit) {
544  animator.add_animation(secondary_unit, "sheath_weapon", secondary_loc, primary_loc, 0, false, "", {0, 0, 0},
545  unit_animation::hit_type::INVALID, secondary_attack, primary_attack, 0);
546  }
547 
548  if(primary_unit || secondary_unit) {
549  animator.start_animations();
550  animator.wait_for_end();
551  }
552 
553  if(primary_unit) {
554  primary_unit->anim_comp().set_standing();
555  }
556 
557  if(secondary_unit) {
558  secondary_unit->anim_comp().set_standing();
559  }
560 
561  reset_helpers(primary_unit, secondary_unit);
562 }
563 
564 void unit_die(const map_location& loc,
565  unit& loser,
566  const_attack_ptr attack,
567  const_attack_ptr secondary_attack,
568  const map_location& winner_loc,
569  unit* winner)
570 {
572  if(do_not_show_anims(disp) || disp->fogged(loc) || !preferences::show_combat()) {
573  return;
574  }
575 
576  unit_animator animator;
577 
578  // hide the hp/xp bars of the loser (useless and prevent bars around an erased unit)
579  animator.add_animation(&loser, "death", loc, winner_loc, 0, false, "", {0, 0, 0},
580  unit_animation::hit_type::KILL, attack, secondary_attack, 0);
581 
582  // but show the bars of the winner (avoid blinking and show its xp gain)
583  animator.add_animation(winner, "victory", winner_loc, loc, 0, true, "", {0, 0, 0},
584  unit_animation::hit_type::KILL, secondary_attack, attack, 0);
585 
586  animator.start_animations();
587  animator.wait_for_end();
588 
589  reset_helpers(winner, &loser);
590 
592  mousehandler->invalidate_reachmap();
593  }
594 }
595 
596 void unit_attack(display* disp,
597  game_board& board,
598  const map_location& a,
599  const map_location& b,
600  int damage,
601  const attack_type& attack,
602  const_attack_ptr secondary_attack,
603  int swing,
604  const std::string& hit_text,
605  int drain_amount,
606  const std::string& att_text,
607  const std::vector<std::string>* extra_hit_sounds)
608 {
609  if(do_not_show_anims(disp) || (disp->fogged(a) && disp->fogged(b)) || !preferences::show_combat()) {
610  return;
611  }
612 
613  // const unit_map& units = disp->get_units();
615 
616  // scroll such that there is at least half a hex spacing around fighters
617  disp->scroll_to_tiles(a, b, game_display::ONSCREEN, true, 0.5, false);
618 
619  log_scope("unit_attack");
620 
621  const unit_map::const_iterator att = board.units().find(a);
622  assert(att.valid());
623  const unit& attacker = *att;
624 
625  const unit_map::iterator def = board.find_unit(b);
626  assert(def.valid());
627 
628  unit& defender = *def;
629  int def_hitpoints = defender.hitpoints();
630 
631  att->set_facing(a.get_relative_dir(b));
632  def->set_facing(b.get_relative_dir(a));
633  defender.set_facing(b.get_relative_dir(a));
634 
635  std::string text = number_and_text(damage, hit_text);
636  std::string text_2 = number_and_text(std::abs(drain_amount), att_text);
637 
638  unit_animation::hit_type hit_type;
639  if(damage >= defender.hitpoints()) {
640  hit_type = unit_animation::hit_type::KILL;
641  } else if(damage > 0) {
642  hit_type = unit_animation::hit_type::HIT;
643  } else {
644  hit_type = unit_animation::hit_type::MISS;
645  }
646 
647  unit_animator animator;
648 
649  animator.add_animation(&attacker, "attack", att->get_location(), def->get_location(), damage, true, text_2,
650  (drain_amount >= 0) ? color_t(0, 255, 0) : color_t(255, 0, 0), hit_type, attack.shared_from_this(),
651  secondary_attack, swing);
652 
653  // note that we take an anim from the real unit, we'll use it later
654  const unit_animation* defender_anim = def->anim_comp().choose_animation(*disp, def->get_location(), "defend",
655  att->get_location(), damage, hit_type, attack.shared_from_this(), secondary_attack, swing);
656 
657  animator.add_animation(&defender, defender_anim, def->get_location(), true, text, {255, 0, 0});
658 
659  for(const unit_ability& ability : attacker.get_abilities("leadership")) {
660  if(ability.second == a) {
661  continue;
662  }
663 
664  if(ability.second == b) {
665  continue;
666  }
667 
668  unit_map::const_iterator leader = board.units().find(ability.second);
669  assert(leader.valid());
670  leader->set_facing(ability.second.get_relative_dir(a));
671 
672  animator.add_animation(&*leader, "leading", ability.second, att->get_location(), damage, true, "", {0, 0, 0},
673  hit_type, attack.shared_from_this(), secondary_attack, swing);
674  }
675 
676  for(const unit_ability& ability : defender.get_abilities("resistance")) {
677  if(ability.second == a) {
678  continue;
679  }
680 
681  if(ability.second == b) {
682  continue;
683  }
684 
685  unit_map::const_iterator helper = board.units().find(ability.second);
686  assert(helper.valid());
687  helper->set_facing(ability.second.get_relative_dir(b));
688 
689  animator.add_animation(&*helper, "resistance", ability.second, def->get_location(), damage, true, "", {0, 0, 0},
690  hit_type, attack.shared_from_this(), secondary_attack, swing);
691  }
692 
693  animator.start_animations();
694  animator.wait_until(0);
695  int damage_left = damage;
696  bool extra_hit_sounds_played = false;
697 
698  while(damage_left > 0 && !animator.would_end()) {
699  if(!extra_hit_sounds_played && extra_hit_sounds != nullptr) {
700  for(std::string hit_sound : *extra_hit_sounds) {
701  sound::play_sound(hit_sound);
702  }
703 
704  extra_hit_sounds_played = true;
705  }
706 
707  int step_left = (animator.get_end_time() - animator.get_animation_time()) / 50;
708  if(step_left < 1) {
709  step_left = 1;
710  }
711 
712  int removed_hp = damage_left / step_left;
713  if(removed_hp < 1) {
714  removed_hp = 1;
715  }
716 
717  defender.take_hit(removed_hp);
718  damage_left -= removed_hp;
719 
720  animator.wait_until(animator.get_animation_time_potential() + 50);
721  }
722 
723  animator.wait_for_end();
724 
725  // pass the animation back to the real unit
726  def->anim_comp().start_animation(animator.get_end_time(), defender_anim, true);
727  reset_helpers(&*att, &*def);
728  def->set_hitpoints(def_hitpoints);
729 }
730 
731 // private helper function, set all helpers to default position
732 void reset_helpers(const unit* attacker, const unit* defender)
733 {
735  const unit_map& units = disp->get_units();
736 
737  if(attacker) {
738  for(const unit_ability& ability : attacker->get_abilities("leadership")) {
739  unit_map::const_iterator leader = units.find(ability.second);
740  assert(leader != units.end());
741  leader->anim_comp().set_standing();
742  }
743  }
744 
745  if(defender) {
746  for(const unit_ability& ability : defender->get_abilities("resistance")) {
747  unit_map::const_iterator helper = units.find(ability.second);
748  assert(helper != units.end());
749  helper->anim_comp().set_standing();
750  }
751  }
752 }
753 
754 void unit_recruited(const map_location& loc, const map_location& leader_loc)
755 {
757  if(do_not_show_anims(disp) || disp->fogged(loc)) {
758  return;
759  }
760 
761  unit_map::const_iterator u = disp->get_units().find(loc);
762  if(u == disp->get_units().end()) {
763  return;
764  }
765 
766  u->set_hidden(true);
767 
768  unit_animator animator;
769 
770  if(leader_loc != map_location::null_location()) {
771  unit_map::const_iterator leader = disp->get_units().find(leader_loc);
772  if(leader == disp->get_units().end()) {
773  return;
774  }
775 
776  disp->scroll_to_tiles(loc, leader_loc, game_display::ONSCREEN, true, 0.0, false);
777  leader->set_facing(leader_loc.get_relative_dir(loc));
778  animator.add_animation(&*leader, "recruiting", leader_loc, loc, 0, true);
779  } else {
780  disp->scroll_to_tile(loc, game_display::ONSCREEN, true, false);
781  }
782 
783  u->set_hidden(false);
784 
785  animator.add_animation(&*u, "recruited", loc, leader_loc);
786  animator.start_animations();
787  animator.wait_for_end();
788  animator.set_all_standing();
789 }
790 
791 void unit_healing(unit& healed, const std::vector<unit*>& healers, int healing, const std::string& extra_text)
792 {
794  const map_location& healed_loc = healed.get_location();
795 
796  if(do_not_show_anims(disp) || disp->fogged(healed_loc)) {
797  return;
798  }
799 
800  // This is all the pretty stuff.
801  disp->scroll_to_tile(healed_loc, game_display::ONSCREEN, true, false);
802  disp->display_unit_hex(healed_loc);
803  unit_animator animator;
804 
805  for(unit* h : healers) {
806  h->set_facing(h->get_location().get_relative_dir(healed_loc));
807  animator.add_animation(h, "healing", h->get_location(), healed_loc, healing);
808  }
809 
810  if(healing < 0) {
811  animator.add_animation(&healed, "poisoned", healed_loc, map_location::null_location(), -healing, false,
812  number_and_text(-healing, extra_text), {255, 0, 0});
813  } else if(healing > 0) {
814  animator.add_animation(&healed, "healed", healed_loc, map_location::null_location(), healing, false,
815  number_and_text(healing, extra_text), {0, 255, 0});
816  } else {
817  animator.add_animation(&healed, "healed", healed_loc, map_location::null_location(), 0, false,
818  extra_text, {0, 255, 0});
819  }
820 
821  animator.start_animations();
822  animator.wait_for_end();
823  animator.set_all_standing();
824 }
825 
826 } // end unit_display namespace
void wait_for_end() const
Definition: animation.cpp:1391
Game board class.
Definition: game_board.hpp:50
virtual void select_hex(map_location hex)
Definition: display.cpp:908
unit_iterator end()
Definition: map.hpp:415
bool update_locked() const
Whether the screen has been &#39;locked&#39; or not.
Definition: video.cpp:292
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:99
void start_animations()
Definition: animation.cpp:1332
virtual const unit_map & units() const override
Definition: game_board.hpp:114
DIRECTION get_relative_dir(const map_location &loc, map_location::RELATIVE_DIR_MODE mode) const
Definition: location.cpp:225
game_display *const disp_
Definition: udisplay.hpp:62
void unit_draw_weapon(const map_location &loc, unit &attacker, const_attack_ptr attack, const_attack_ptr secondary_attack, const map_location &defender_loc, unit *defender)
Play a pre-fight animation First unit is the attacker, second unit the defender.
Definition: udisplay.cpp:499
This class represents a single unit of a specific type.
Definition: unit.hpp:99
internal_ptr get_unit_ptr()
Get a copy of the internal unit pointer.
bool would_end() const
Definition: animation.cpp:1356
#define a
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
void set_location(const map_location &loc)
Sets this unit&#39;s map location.
Definition: unit.hpp:1184
const unit_map & get_units() const
Definition: display.hpp:157
int viewing_side() const
Definition: display.hpp:133
unit_mover(const unit_mover &)=delete
#define h
void set_facing(map_location::DIRECTION dir) const
The this unit&#39;s facing.
Definition: unit.cpp:1517
int get_animation_time_potential() const
Definition: animation.cpp:1413
void play_sound(const std::string &files, channel_group group, unsigned int repeats)
Definition: sound.cpp:980
void draw_debugging_aids()
Definition: display.cpp:807
Contains a number of free functions which display units.
Definition: udisplay.cpp:37
void wait_until(int animation_time) const
Definition: animation.cpp:1366
#define b
unit_ptr shown_unit_
The animation potential to wait until. INT_MIN for no wait; INT_MAX to wait for end.
Definition: udisplay.hpp:68
team & get_team(int i)
Definition: game_board.hpp:104
static mouse_handler * get_singleton()
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
void finish(unit_ptr u, map_location::DIRECTION dir=map_location::NDIRECTIONS)
Finishes the display of movement for the supplied unit.
Definition: udisplay.cpp:425
std::pair< const config *, map_location > unit_ability
Definition: unit.hpp:48
void restart_animation()
Definition: animation.cpp:1439
bool tile_fully_on_screen(const map_location &loc) const
Check if a tile is fully visible on screen.
Definition: display.cpp:1086
const bool force_scroll_
Definition: udisplay.hpp:65
unit_animator animator_
Definition: udisplay.hpp:66
void update_shown_unit()
Switches the display back to *shown_unit_ after animating.
Definition: udisplay.cpp:248
game_board * gameboard
Definition: resources.cpp:20
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
void unit_die(const map_location &loc, unit &loser, const_attack_ptr attack, const_attack_ptr secondary_attack, const map_location &winner_loc, unit *winner)
Show a unit fading out.
Definition: udisplay.cpp:564
fake_unit_manager * fake_units
Definition: resources.cpp:30
A class to encapsulate the steps of drawing a unit&#39;s move.
Definition: udisplay.hpp:44
void unit_healing(unit &healed, const std::vector< unit *> &healers, int healing, const std::string &extra_text)
This will use a poisoning anim if healing<0.
Definition: udisplay.cpp:791
const std::vector< map_location > & path_
The unit to be (re-)shown after an animation finishes.
Definition: udisplay.hpp:69
void wait_for_anims()
Waits for the final animation of the most recent proceed_to() to finish.
Definition: udisplay.cpp:399
bool show_combat()
Definition: game.cpp:446
bool fogged(const map_location &loc) const
Returns true if location (x,y) is covered in fog.
Definition: display.cpp:614
Encapsulates the map of the game.
Definition: location.hpp:42
unit_iterator find(std::size_t id)
Definition: map.cpp:311
bool faked() const
Definition: video.hpp:65
bool tiles_adjacent(const map_location &a, const map_location &b)
Function which tells if two locations are adjacent.
Definition: location.cpp:557
unit * get()
Get a raw pointer to the underlying unit.
void replace_temporary(unit_ptr u)
Makes the temporary unit used by this match the supplied unit.
Definition: udisplay.cpp:222
unit_animation_component & anim_comp() const
Definition: unit.hpp:1361
int get_end_time() const
Definition: animation.cpp:1418
void scroll_to_tile(const map_location &loc, SCROLL_TYPE scroll_type=ONSCREEN, bool check_fogged=true, bool force=true)
Scroll such that location loc is on-screen.
Definition: display.cpp:1192
void unit_sheath_weapon(const map_location &primary_loc, unit *primary_unit, const_attack_ptr primary_attack, const_attack_ptr secondary_attack, const map_location &secondary_loc, unit *secondary_unit)
Play a post-fight animation Both unit can be set to null, only valid units will play their animation...
Definition: udisplay.cpp:525
void scroll_to_tiles(map_location loc1, map_location loc2, SCROLL_TYPE scroll_type=ONSCREEN, bool check_fogged=true, double add_spacing=0.0, bool force=true)
Scroll such that location loc1 is on-screen.
Definition: display.cpp:1205
void pause_animation()
Definition: animation.cpp:1430
void proceed_to(unit_ptr u, std::size_t path_index, bool update=false, bool wait=true)
Visually moves a unit from the last hex we drew to the one specified by path_index.
Definition: udisplay.cpp:320
unit_map::iterator find_unit(const map_location &loc)
Definition: game_board.hpp:174
void start(unit_ptr u)
Initiates the display of movement for the supplied unit.
Definition: udisplay.cpp:262
#define log_scope(description)
Definition: log.hpp:186
DIRECTION
Valid directions which can be moved in our hexagonal world.
Definition: location.hpp:44
unit_ability_list get_abilities(const std::string &tag_name, const map_location &loc, const_attack_ptr weapon=nullptr, const_attack_ptr opp_weapon=nullptr) const
Gets the unit&#39;s active abilities of a particular type if it were on a specified location.
Definition: abilities.cpp:180
void reset_helpers(const unit *attacker, const unit *defender)
Definition: udisplay.cpp:732
int get_animation_time() const
Definition: animation.cpp:1408
boost::intrusive_ptr< unit > unit_ptr
Definition: ptr.hpp:29
void set_all_standing()
Definition: animation.cpp:1448
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1174
void unit_recruited(const map_location &loc, const map_location &leader_loc)
Definition: udisplay.cpp:754
Standard logging facilities (interface).
fake_unit_ptr temp_unit_ptr_
Definition: udisplay.hpp:71
void run_event_loop()
Definition: events.cpp:436
CVideo & video()
Gets the underlying screen object.
Definition: display.hpp:237
static const map_location & null_location()
Definition: location.hpp:85
Container associating units to locations.
Definition: map.hpp:99
void set_standing(bool with_bars=true)
Sets the animation state to standing.
void unit_attack(display *disp, game_board &board, const map_location &a, const map_location &b, int damage, const attack_type &attack, const_attack_ptr secondary_attack, int swing, const std::string &hit_text, int drain_amount, const std::string &att_text, const std::vector< std::string > *extra_hit_sounds)
Make the unit on tile &#39;a&#39; attack the unit on tile &#39;b&#39;.
Definition: udisplay.cpp:596
bool valid() const
Definition: map.hpp:276
std::shared_ptr< const attack_type > const_attack_ptr
Definition: ptr.hpp:37
void display_unit_hex(map_location hex)
Change the unit to be displayed in the sidebar.
std::string path
File path.
Holds a temporary unit that can be drawn on the map without being placed in the unit_map.
Display units performing various actions: moving, attacking, and dying.
void move_unit(const std::vector< map_location > &path, unit_ptr u, bool animate, map_location::DIRECTION dir, bool force_scroll)
Display a unit moving along a given path.
Definition: udisplay.cpp:488
static game_display * get_singleton()