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