The Battle for Wesnoth  1.13.4+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 - 2016 by David White <[email protected]>
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 "global.hpp"
18 #include "units/udisplay.hpp"
19 
20 #include "fake_unit_manager.hpp"
21 #include "fake_unit_ptr.hpp"
22 #include "game_board.hpp"
23 #include "game_display.hpp"
24 #include "game_preferences.hpp"
25 #include "log.hpp"
26 #include "mouse_events.hpp"
27 #include "resources.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,unit_animation::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
144  target_time += 200;
145  target_time -= target_time%200;
146 
147  // This code causes backwards teleport because the time > 200 causes offset > 1.0
148  // which will not match with the following -1.0
149  // if( target_time - animator.get_animation_time_potential() < 100 ) target_time +=200;
150 
151  return target_time;
152 }
153 
154 namespace unit_display
155 {
156 
157 /**
158  * The path must remain unchanged for the life of this object.
159  */
160 unit_mover::unit_mover(const std::vector<map_location>& path, bool animate, bool force_scroll) :
161  disp_(game_display::get_singleton()),
162  can_draw_(disp_ && !disp_->video().update_locked() &&
163  !disp_->video().faked() && path.size() > 1),
164  animate_(animate),
165  force_scroll_(force_scroll),
166  animator_(),
167  wait_until_(INT_MIN),
168  shown_unit_(),
169  path_(path),
170  current_(0),
171  temp_unit_ptr_(),
172  // Somewhat arbitrary default values.
173  was_hidden_(false),
174  is_enemy_(true)
175 {
176  // Some error conditions that indicate something has gone very wrong.
177  // (This class can handle these conditions, but someone wanted them
178  // to be assertions.)
179  assert(!path_.empty());
180  assert(disp_);
181 }
182 
183 
185 {
186  // Make sure a unit hidden for movement is unhidden.
188  // For safety, clear the animator before deleting the temp unit.
189  animator_.clear();
190 }
191 
192 
193 /**
194  * Makes the temporary unit used by this match the supplied unit.
195  * This is called when setting the initial unit, as well as replacing it with
196  * something new.
197  * When this finishes, the supplied unit is hidden, while the temporary unit
198  * is not hidden.
199  */
200 /* Note: Hide the unit in its current location; do not actually remove it.
201  * Otherwise the status displays will be wrong during the movement.
202  */
204 {
205  if ( disp_ == nullptr )
206  // No point in creating a temp unit with no way to display it.
207  return;
208 
209  // Save the hidden state of the unit.
210  was_hidden_ = u->get_hidden();
211 
212  // Make our temporary unit mostly match u...
214 
215  // ... but keep the temporary unhidden and hide the original.
216  temp_unit_ptr_->set_hidden(false);
217  u->set_hidden(true);
218 
219  // Update cached data.
220  is_enemy_ = (*resources::teams)[u->side()-1].is_enemy(disp_->viewing_side());
221 }
222 
223 
224 /**
225  * Switches the display back to *shown_unit_ after animating.
226  * This uses temp_unit_ptr_, so (in the destructor) call this before deleting
227  * temp_unit_ptr_.
228  */
230 {
231  if ( shown_unit_ ) {
232  // Switch the display back to the real unit.
233  shown_unit_->set_hidden(was_hidden_);
234  temp_unit_ptr_->set_hidden(true);
235  shown_unit_.reset();
236  }
237 }
238 
239 
240 /**
241  * Initiates the display of movement for the supplied unit.
242  * This should be called before attempting to display moving to a new hex.
243  */
245 {
246  // Nothing to do here if there is nothing to animate.
247  if ( !can_draw_ )
248  return;
249  // If no animation then hide unit until end of movement
250  if ( !animate_ ) {
251  was_hidden_ = u->get_hidden();
252  u->set_hidden(true);
253  return;
254  }
255 
256  // This normally does nothing, but just in case...
257  wait_for_anims();
258 
259  // Visually replace the original unit with the temporary.
260  // (Original unit is left on the map, so the unit count is correct.)
262 
263  // Initialize our temporary unit for the move.
264  temp_unit_ptr_->set_location(path_[0]);
265  temp_unit_ptr_->set_facing(path_[0].get_relative_dir(path_[1]));
266  temp_unit_ptr_->anim_comp().set_standing(false);
267  disp_->invalidate(path_[0]);
268 
269  // If the unit can be seen here by the viewing side:
270  if ( !is_enemy_ || !temp_unit_ptr_->invisible(path_[0]) ) {
271  // Scroll to the path, but only if it fully fits on screen.
272  // If it does not fit we might be able to do a better scroll later.
273  disp_->scroll_to_tiles(path_, game_display::ONSCREEN, true, true, 0.0, false);
274  }
275  // We need to clear big invalidation before the move and have a smooth animation
276  // (mainly black stripes and invalidation after canceling attack dialog).
277  // Two draw calls are needed to also redraw the previously invalidated hexes.
278  // We use update=false because we don't need delay here (no time wasted)
279  // and no screen refresh (will be done by last 3rd draw() and it optimizes
280  // the double blitting done by these invalidations).
281  disp_->draw(false);
282  disp_->draw(false);
283 
284  // The last draw() was still slow, and its initial new_animation_frame() call
285  // is now old, so we do another draw() to get a fresh one
286  // TODO: replace that by a new_animation_frame() before starting anims
287  // don't forget to change the previous draw(false) to true
288  disp_->draw(true);
289 
290  // extra immobile movement animation for take-off
291  animator_.add_animation(temp_unit_ptr_.get(), "pre_movement", path_[0], path_[1]);
294  animator_.clear();
295 
296  // Switch the display back to the real unit.
297  u->set_facing(temp_unit_ptr_->facing());
298  u->anim_comp().set_standing(false); // Need to reset u's animation so the new facing takes effect.
299  u->set_hidden(was_hidden_);
300  temp_unit_ptr_->set_hidden(true);
301 }
302 
303 
304 /**
305  * Visually moves a unit from the last hex we drew to the one specified by
306  * @a path_index. If @a path_index points to an earlier hex, we do nothing.
307  * The moving unit will only be updated if update is set to true; otherwise,
308  * the provided unit is merely hidden during the movement and re-shown after.
309  * (Not updating the unit can produce smoother animations in some cases.)
310  * If @a wait is set to false, this returns without waiting for the final
311  * animation to finish. Call wait_for_anims() to explicitly get this final
312  * wait (another call to proceed_to() or finish() will implicitly wait). The
313  * unit must remain valid until the wait is finished.
314  */
315 void unit_mover::proceed_to(unit_ptr u, size_t path_index, bool update, bool wait)
316 {
317  // Nothing to do here if animations cannot be shown.
318  if ( !can_draw_ || !animate_ )
319  return;
320 
321  // Handle pending visibility issues before introducing new ones.
322  wait_for_anims();
323 
324  if ( update || !temp_unit_ptr_ )
325  // Replace the temp unit (which also hides u and shows our temporary).
327  else
328  {
329  // Just switch the display from the real unit to our fake one.
330  temp_unit_ptr_->set_hidden(false);
331  u->set_hidden(true);
332  }
333 
334  // Safety check.
335  path_index = std::min(path_index, path_.size()-1);
336 
337  for ( ; current_ < path_index; ++current_ )
338  // If the unit can be seen by the viewing side while making this step:
339  if ( !is_enemy_ || !temp_unit_ptr_->invisible(path_[current_]) ||
340  !temp_unit_ptr_->invisible(path_[current_+1]) )
341  {
342  // Wait for the previous step to complete before drawing the next one.
343  wait_for_anims();
344 
345  if ( !disp_->tile_fully_on_screen(path_[current_]) ||
346  !disp_->tile_fully_on_screen(path_[current_+1]))
347  {
348  // prevent the unit from disappearing if we scroll here with i == 0
349  temp_unit_ptr_->set_location(path_[current_]);
350  disp_->invalidate(path_[current_]);
351  // scroll in as much of the remaining path as possible
352  if ( temp_unit_ptr_->anim_comp().get_animation() )
353  temp_unit_ptr_->anim_comp().get_animation()->pause_animation();
354  disp_->scroll_to_tiles(path_.begin() + current_,
356  true, false, 0.0, force_scroll_);
357  if ( temp_unit_ptr_->anim_comp().get_animation() )
358  temp_unit_ptr_->anim_comp().get_animation()->restart_animation();
359  }
360 
361  if ( tiles_adjacent(path_[current_], path_[current_+1]) )
362  wait_until_ =
363  move_unit_between(path_[current_], path_[current_+1],
365  path_.size() - (current_+2), animator_,
366  *disp_);
367  else if ( path_[current_] != path_[current_+1] )
368  teleport_unit_between(path_[current_], path_[current_+1],
369  *temp_unit_ptr_, *disp_);
370  }
371 
372  // Update the unit's facing.
373  u->set_facing(temp_unit_ptr_->facing());
374  u->anim_comp().set_standing(false); // Need to reset u's animation so the new facing takes effect.
375  // Remember the unit to unhide when the animation finishes.
376  shown_unit_ = u;
377  if ( wait )
378  wait_for_anims();
379 }
380 
381 
382 /**
383  * Waits for the final animation of the most recent proceed_to() to finish.
384  * It is not necessary to call this unless you want to wait before the next
385  * call to proceed_to() or finish().
386  */
388 {
389  if ( wait_until_ == INT_MAX )
390  // Wait for end (not currently used, but still supported).
392  else if ( wait_until_ != INT_MIN ) {
393  // Wait until the specified time (used for normal movement).
395  // debug code, see unit_frame::redraw()
396  // std::cout << " end\n";
397  }
398 
399  // Reset data.
400  wait_until_ = INT_MIN;
401  animator_.clear();
402 
404 }
405 
406 
407 /**
408  * Finishes the display of movement for the supplied unit.
409  * If called before showing the unit reach the end of the path, it will be
410  * assumed that the movement ended early.
411  * If @a dir is not supplied, the final direction will be determined by (the
412  * last two traversed hexes of) the path.
413  */
415 {
416  // Nothing to do here if the display is not valid.
417  if ( !can_draw_ ) {
418  // Make sure to reset the unit's animation to deal with a quirk in the
419  // action engine where it leaves it to us to reenable bars even if the
420  // display is initially locked.
421  u->anim_comp().set_standing(true);
422  return;
423  }
424 
425  const map_location & end_loc = path_[current_];
426  const map_location::DIRECTION final_dir = current_ == 0 ?
427  path_[0].get_relative_dir(path_[1]) :
428  path_[current_-1].get_relative_dir(end_loc);
429 
430  if ( animate_ )
431  {
432  wait_for_anims(); // In case proceed_to() did not wait for the last animation.
433 
434  // Make sure the displayed unit is correct.
436  temp_unit_ptr_->set_location(end_loc);
437  temp_unit_ptr_->set_facing(final_dir);
438 
439  // Animation
440  animator_.add_animation(temp_unit_ptr_.get(), "post_movement", end_loc);
443  animator_.clear();
444 
445  // Switch the display back to the real unit.
446  u->set_hidden(was_hidden_);
447  temp_unit_ptr_->set_hidden(true);
448 
450  if ( mousehandler ) {
451  mousehandler->invalidate_reachmap();
452  }
453  }
454  else
455  {
456  // Show the unit at end of skipped animation
457  u->set_hidden(was_hidden_);
458  }
459 
460  // Facing gets set even when not animating.
461  u->set_facing(dir == map_location::NDIRECTIONS ? final_dir : dir);
462  u->anim_comp().set_standing(true); // Need to reset u's animation so the new facing takes effect.
463 
464  // Redraw path ends (even if not animating).
465  disp_->invalidate(path_.front());
466  disp_->invalidate(end_loc);
467 }
468 
469 
470 /**
471  * Display a unit moving along a given path.
472  *
473  * @param path The path to traverse.
474  * @param u The unit to show being moved. Its facing will be updated,
475  * but not its position.
476  * @param animate If set to false, only side-effects of move are applied
477  * (correct unit facing, path hexes redrawing).
478  * @param dir Unit will be set facing this direction after move.
479  * If nothing passed, direction will be set based on path.
480  */
481 /* Note: Hide the unit in its current location,
482  * but don't actually remove it until the move is done,
483  * so that while the unit is moving status etc.
484  * will still display the correct number of units.
485  */
486 void move_unit(const std::vector<map_location>& path, unit_ptr u,
487  bool animate, map_location::DIRECTION dir,
488  bool force_scroll)
489 {
490  unit_mover mover(path, animate, force_scroll);
491 
492  mover.start(u);
493  mover.proceed_to(u, path.size());
494  mover.finish(u, dir);
495 }
496 
497 
498 void reset_helpers(const unit *attacker,const unit *defender);
499 
500 void unit_draw_weapon(const map_location& loc, unit& attacker,
501  const attack_type* attack,const attack_type* secondary_attack, const map_location& defender_loc,unit* defender)
502 {
504  if(!disp ||disp->video().update_locked() || disp->video().faked() || disp->fogged(loc) || preferences::show_combat() == false) {
505  return;
506  }
507  unit_animator animator;
508  attacker.set_facing(loc.get_relative_dir(defender_loc));
509  defender->set_facing(defender_loc.get_relative_dir(loc));
510  animator.add_animation(&attacker,"draw_weapon",loc,defender_loc,0,false,"",0,unit_animation::HIT,attack,secondary_attack,0);
511  animator.add_animation(defender,"draw_weapon",defender_loc,loc,0,false,"",0,unit_animation::MISS,secondary_attack,attack,0);
512  animator.start_animations();
513  animator.wait_for_end();
514 
515 }
516 
517 
518 void unit_sheath_weapon(const map_location& primary_loc, unit* primary_unit,
519  const attack_type* primary_attack,const attack_type* secondary_attack, const map_location& secondary_loc,unit* secondary_unit)
520 {
522  if(!disp ||disp->video().update_locked() || disp->video().faked() || disp->fogged(primary_loc) || preferences::show_combat() == false) {
523  return;
524  }
525  unit_animator animator;
526  if(primary_unit) {
527  animator.add_animation(primary_unit,"sheath_weapon",primary_loc,secondary_loc,0,false,"",0,unit_animation::INVALID,primary_attack,secondary_attack,0);
528  }
529  if(secondary_unit) {
530  animator.add_animation(secondary_unit,"sheath_weapon",secondary_loc,primary_loc,0,false,"",0,unit_animation::INVALID,secondary_attack,primary_attack,0);
531  }
532 
533  if(primary_unit || secondary_unit) {
534  animator.start_animations();
535  animator.wait_for_end();
536  }
537  if(primary_unit) {
538  primary_unit->anim_comp().set_standing();
539  }
540  if(secondary_unit) {
541  secondary_unit->anim_comp().set_standing();
542  }
543  reset_helpers(primary_unit,secondary_unit);
544 
545 }
546 
547 
548 void unit_die(const map_location& loc, unit& loser,
549  const attack_type* attack,const attack_type* secondary_attack, const map_location& winner_loc,unit* winner)
550 {
552  if(!disp ||disp->video().update_locked() || disp->video().faked() || disp->fogged(loc) || preferences::show_combat() == false) {
553  return;
554  }
555  unit_animator animator;
556  // hide the hp/xp bars of the loser (useless and prevent bars around an erased unit)
557  animator.add_animation(&loser,"death",loc,winner_loc,0,false,"",0,unit_animation::KILL,attack,secondary_attack,0);
558  // but show the bars of the winner (avoid blinking and show its xp gain)
559  animator.add_animation(winner,"victory",winner_loc,loc,0,true,"",0,
560  unit_animation::KILL,secondary_attack,attack,0);
561  animator.start_animations();
562  animator.wait_for_end();
563 
564  reset_helpers(winner,&loser);
566  if (mousehandler) {
567  mousehandler->invalidate_reachmap();
568  }
569 }
570 
571 
572 void unit_attack(display * disp, game_board & board,
573  const map_location& a, const map_location& b, int damage,
574  const attack_type& attack, const attack_type* secondary_attack,
575  int swing,std::string hit_text,int drain_amount,std::string att_text, const std::vector<std::string>* extra_hit_sounds)
576 {
577  if(!disp ||disp->video().update_locked() || disp->video().faked() ||
578  (disp->fogged(a) && disp->fogged(b)) || preferences::show_combat() == false) {
579  return;
580  }
581  //const unit_map& units = disp->get_units();
583 
584  // scroll such that there is at least half a hex spacing around fighters
585  disp->scroll_to_tiles(a,b,game_display::ONSCREEN,true,0.5,false);
586 
587  log_scope("unit_attack");
588 
589  const unit_map::const_iterator att = board.units().find(a);
590  assert(att.valid());
591  const unit& attacker = *att;
592 
593  const unit_map::iterator def = board.find_unit(b);
594  assert(def.valid());
595  unit &defender = *def;
596  int def_hitpoints = defender.hitpoints();
597 
598  att->set_facing(a.get_relative_dir(b));
599  def->set_facing(b.get_relative_dir(a));
600  defender.set_facing(b.get_relative_dir(a));
601 
602 
603  unit_animator animator;
604  unit_ability_list leaders = attacker.get_abilities("leadership");
605  unit_ability_list helpers = defender.get_abilities("resistance");
606 
607  std::string text = number_and_text(damage, hit_text);
608  std::string text_2 = number_and_text(abs(drain_amount), att_text);
609 
610  unit_animation::hit_type hit_type;
611  if(damage >= defender.hitpoints()) {
612  hit_type = unit_animation::KILL;
613  } else if(damage > 0) {
614  hit_type = unit_animation::HIT;
615  }else {
616  hit_type = unit_animation::MISS;
617  }
618  animator.add_animation(&attacker, "attack", att->get_location(),
619  def->get_location(), damage, true, text_2,
620  (drain_amount >= 0) ? display::rgb(0, 255, 0) : display::rgb(255, 0, 0),
621  hit_type, &attack, secondary_attack, swing);
622 
623  // note that we take an anim from the real unit, we'll use it later
624  const unit_animation *defender_anim = def->anim_comp().choose_animation(*disp,
625  def->get_location(), "defend", att->get_location(), damage,
626  hit_type, &attack, secondary_attack, swing);
627  animator.add_animation(&defender, defender_anim, def->get_location(),
628  true, text , display::rgb(255, 0, 0));
629 
630  for (const unit_ability & ability : leaders) {
631  if(ability.second == a) continue;
632  if(ability.second == b) continue;
633  unit_map::const_iterator leader = board.units().find(ability.second);
634  assert(leader.valid());
635  leader->set_facing(ability.second.get_relative_dir(a));
636  animator.add_animation(&*leader, "leading", ability.second,
637  att->get_location(), damage, true, "", 0,
638  hit_type, &attack, secondary_attack, swing);
639  }
640  for (const unit_ability & ability : helpers) {
641  if(ability.second == a) continue;
642  if(ability.second == b) continue;
643  unit_map::const_iterator helper = board.units().find(ability.second);
644  assert(helper.valid());
645  helper->set_facing(ability.second.get_relative_dir(b));
646  animator.add_animation(&*helper, "resistance", ability.second,
647  def->get_location(), damage, true, "", 0,
648  hit_type, &attack, secondary_attack, swing);
649  }
650 
651 
652  animator.start_animations();
653  animator.wait_until(0);
654  int damage_left = damage;
655  bool extra_hit_sounds_played = false;
656  while(damage_left > 0 && !animator.would_end()) {
657  if(!extra_hit_sounds_played && extra_hit_sounds != nullptr) {
658  for (std::string hit_sound : *extra_hit_sounds) {
659  sound::play_sound(hit_sound);
660  }
661  extra_hit_sounds_played = true;
662  }
663 
664  int step_left = (animator.get_end_time() - animator.get_animation_time() )/50;
665  if(step_left < 1) step_left = 1;
666  int removed_hp = damage_left/step_left ;
667  if(removed_hp < 1) removed_hp = 1;
668  defender.take_hit(removed_hp);
669  damage_left -= removed_hp;
670  animator.wait_until(animator.get_animation_time_potential() +50);
671  }
672  animator.wait_for_end();
673  // pass the animation back to the real unit
674  def->anim_comp().start_animation(animator.get_end_time(), defender_anim, true);
675  reset_helpers(&*att, &*def);
676  def->set_hitpoints(def_hitpoints);
677 }
678 
679 // private helper function, set all helpers to default position
680 void reset_helpers(const unit *attacker,const unit *defender)
681 {
683  const unit_map& units = disp->get_units();
684  if(attacker) {
685  unit_ability_list leaders = attacker->get_abilities("leadership");
686  for (const unit_ability & ability : leaders) {
687  unit_map::const_iterator leader = units.find(ability.second);
688  assert(leader != units.end());
689  leader->anim_comp().set_standing();
690  }
691  }
692 
693  if(defender) {
694  unit_ability_list helpers = defender->get_abilities("resistance");
695  for (const unit_ability & ability : helpers) {
696  unit_map::const_iterator helper = units.find(ability.second);
697  assert(helper != units.end());
698  helper->anim_comp().set_standing();
699  }
700  }
701 }
702 
703 void unit_recruited(const map_location& loc,const map_location& leader_loc)
704 {
706  if(!disp || disp->video().update_locked() || disp->video().faked() ||disp->fogged(loc)) return;
707  unit_map::const_iterator u = disp->get_units().find(loc);
708  if(u == disp->get_units().end()) return;
709  u->set_hidden(true);
710 
711  unit_animator animator;
712  if(leader_loc != map_location::null_location()) {
713  unit_map::const_iterator leader = disp->get_units().find(leader_loc);
714  if(leader == disp->get_units().end()) return;
715  disp->scroll_to_tiles(loc,leader_loc,game_display::ONSCREEN,true,0.0,false);
716  leader->set_facing(leader_loc.get_relative_dir(loc));
717  animator.add_animation(&*leader, "recruiting", leader_loc, loc, 0, true);
718  } else {
719  disp->scroll_to_tile(loc,game_display::ONSCREEN,true,false);
720  }
721 
722  disp->draw();
723  u->set_hidden(false);
724  animator.add_animation(&*u, "recruited", loc, leader_loc);
725  animator.start_animations();
726  animator.wait_for_end();
727  animator.set_all_standing();
728  if (loc==disp->mouseover_hex()) disp->invalidate_unit();
729 }
730 
731 void unit_healing(unit &healed, const std::vector<unit *> &healers, int healing,
732  const std::string & extra_text)
733 {
735  const map_location &healed_loc = healed.get_location();
736  if(!disp || disp->video().update_locked() || disp->video().faked() || disp->fogged(healed_loc)) return;
737 
738  // This is all the pretty stuff.
739  disp->scroll_to_tile(healed_loc, game_display::ONSCREEN,true,false);
740  disp->display_unit_hex(healed_loc);
741  unit_animator animator;
742 
743  for (unit *h : healers) {
744  h->set_facing(h->get_location().get_relative_dir(healed_loc));
745  animator.add_animation(h, "healing", h->get_location(),
746  healed_loc, healing);
747  }
748 
749  if (healing < 0) {
750  animator.add_animation(&healed, "poisoned", healed_loc,
751  map_location::null_location(), -healing, false,
752  number_and_text(-healing, extra_text),
753  display::rgb(255,0,0));
754  } else if ( healing > 0 ) {
755  animator.add_animation(&healed, "healed", healed_loc,
756  map_location::null_location(), healing, false,
757  number_and_text(healing, extra_text),
758  display::rgb(0,255,0));
759  } else {
760  animator.add_animation(&healed, "healed", healed_loc,
761  map_location::null_location(), 0, false,
762  extra_text, display::rgb(0,255,0));
763  }
764  animator.start_animations();
765  animator.wait_for_end();
766  animator.set_all_standing();
767 }
768 
769 void wml_animation_internal(unit_animator &animator, const vconfig &cfg, const map_location &default_location = map_location::null_location());
770 
771 void wml_animation(const vconfig &cfg, const map_location &default_location)
772 {
774  if (disp.video().update_locked() || disp.video().faked()) return;
775  unit_animator animator;
776  wml_animation_internal(animator, cfg, default_location);
777  animator.start_animations();
778  animator.wait_for_end();
779  animator.set_all_standing();
780 }
781 
782 void wml_animation_internal(unit_animator &animator, const vconfig &cfg, const map_location &default_location)
783 {
784  unit_const_ptr u;
785 
786  unit_map::const_iterator u_it = resources::units->find(default_location);
787  if (u_it.valid()) {
788  u = u_it.get_shared_ptr();
789  }
790 
791  // Search for a valid unit filter,
792  // and if we have one, look for the matching unit
793  vconfig filter = cfg.child("filter");
794  if(!filter.null()) {
795  const unit_filter ufilt(filter, resources::filter_con);
796  u = ufilt.first_match_on_map();
797  }
798 
799  // We have found a unit that matches the filter
800  if (u && !resources::screen->fogged(u->get_location()))
801  {
802  attack_type *primary = nullptr;
803  attack_type *secondary = nullptr;
804  Uint32 text_color;
806  std::vector<attack_type> attacks = u->attacks();
808  boost::scoped_ptr<attack_type> dummy_primary;
809  boost::scoped_ptr<attack_type> dummy_secondary;
810 
811  // death and victory animations are handled here because usually
812  // the code iterates through all the unit's attacks
813  // but in these two specific cases we need to create dummy attacks
814  // to fire correctly certain animations
815  // this is especially evident with the Wose's death animations
816  if (cfg["flag"] == "death" || cfg["flag"] == "victory") {
817  filter = cfg.child("primary_attack");
818  if(!filter.null()) {
819  dummy_primary.reset(new attack_type(filter.get_config()));
820  primary = dummy_primary.get();
821  }
822  filter = cfg.child("secondary_attack");
823  if(!filter.null()) {
824  dummy_secondary.reset(new attack_type(filter.get_config()));
825  secondary = dummy_secondary.get();
826  }
827  }
828 
829  else {
830  filter = cfg.child("primary_attack");
831  if(!filter.null()) {
832  for(itor = attacks.begin(); itor != attacks.end(); ++itor){
833  if(itor->matches_filter(filter.get_parsed_config())) {
834  primary = &*itor;
835  break;
836  }
837  }
838  }
839 
840  filter = cfg.child("secondary_attack");
841  if(!filter.null()) {
842  for(itor = attacks.begin(); itor != attacks.end(); ++itor){
843  if(itor->matches_filter(filter.get_parsed_config())) {
844  secondary = &*itor;
845  break;
846  }
847  }
848  }
849  }
850 
851  if(cfg["hits"] == "yes" || cfg["hits"] == "hit") {
852  hits = unit_animation::HIT;
853  }
854  if(cfg["hits"] == "no" || cfg["hits"] == "miss") {
855  hits = unit_animation::MISS;
856  }
857  if( cfg["hits"] == "kill" ) {
858  hits = unit_animation::KILL;
859  }
860  if(cfg["red"].empty() && cfg["green"].empty() && cfg["blue"].empty()) {
861  text_color = display::rgb(0xff,0xff,0xff);
862  } else {
863  text_color = display::rgb(cfg["red"], cfg["green"], cfg["blue"]);
864  }
865  resources::screen->scroll_to_tile(u->get_location(), game_display::ONSCREEN, true, false);
866  vconfig t_filter = cfg.child("facing");
867  map_location secondary_loc = map_location::null_location();
868  if(!t_filter.empty()) {
870  std::set<map_location> locs;
871  filter.get_locations(locs);
872  if (!locs.empty() && u->get_location() != *locs.begin()) {
873  map_location::DIRECTION dir =u->get_location().get_relative_dir(*locs.begin());
874  u->set_facing(dir);
875  secondary_loc = u->get_location().get_direction(dir);
876  }
877  }
878  config::attribute_value text = u->gender() == unit_race::FEMALE ? cfg["female_text"] : cfg["male_text"];
879  if(text.blank()) {
880  text = cfg["text"];
881  }
882  animator.add_animation(&*u, cfg["flag"], u->get_location(),
883  secondary_loc, cfg["value"], cfg["with_bars"].to_bool(),
884  text.str(), text_color, hits, primary, secondary,
885  cfg["value_second"]);
886  }
887  const vconfig::child_list sub_anims = cfg.get_children("animate");
888  vconfig::child_list::const_iterator anim_itor;
889  for(anim_itor = sub_anims.begin(); anim_itor != sub_anims.end();++anim_itor) {
890  wml_animation_internal(animator, *anim_itor);
891  }
892 
893 }
894 } // end unit_display namespace
Game board class.
Definition: game_board.hpp:55
bool fogged(const map_location &loc) const
Returns true if location (x,y) is covered in fog.
Definition: display.hpp:353
std::string str() const
Definition: config.cpp:353
virtual void select_hex(map_location hex)
Definition: display.cpp:1792
virtual const unit_map & units() const
Definition: game_board.hpp:99
unit_iterator end()
Definition: map.hpp:311
const map_location & get_location() const
Definition: unit.hpp:286
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:88
void start_animations()
Definition: animation.cpp:1346
void wait_until(int animation_time) const
Definition: animation.cpp:1380
bool invalidate(const map_location &loc)
Function to invalidate a specific tile for redrawing.
Definition: display.cpp:3536
game_display *const disp_
Definition: udisplay.hpp:62
Definition: unit.hpp:95
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
void unit_attack(display *disp, game_board &board, const map_location &a, const map_location &b, int damage, const attack_type &attack, const attack_type *secondary_attack, int swing, std::string hit_text, int drain_amount, 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:572
int get_animation_time_potential() const
Definition: animation.cpp:1420
internal_ptr get_unit_ptr()
Get a copy of the internal unit pointer.
vconfig child(const std::string &key) const
Returns a child of *this whose key is key.
Definition: variable.cpp:243
config get_parsed_config() const
Definition: variable.cpp:131
game_display * screen
Definition: resources.cpp:27
void invalidate_unit()
Function to invalidate that unit status displayed on the sidebar.
DIRECTION get_relative_dir(const map_location &loc, map_location::RELATIVE_DIR_MODE mode) const
Definition: location.cpp:220
bool tile_fully_on_screen(const map_location &loc)
Check if a tile is fully visible on screen.
Definition: display.cpp:2338
virtual void draw()
Draws invalidated items.
Definition: display.cpp:2706
const std::string number
template to number regex
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
Variant for storing WML attributes.
Definition: config.hpp:223
void update_display()
Copy the backbuffer to the framebuffer.
Definition: display.cpp:1446
void wml_animation(const vconfig &cfg, const map_location &default_location)
Parse a standard WML for animations and play the corresponding animation.
Definition: udisplay.cpp:771
void play_sound(const std::string &files, channel_group group, unsigned int repeats)
Definition: sound.cpp:818
bool blank() const
Tests for an attribute that was never set.
Definition: config.cpp:367
Contains a number of free functions which display units.
Definition: udisplay.cpp:154
GLdouble GLdouble GLdouble b
Definition: glew.h:6966
bool would_end() const
Definition: animation.cpp:1371
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
unit_mover(const std::vector< map_location > &path, bool animate=true, bool force_scroll=false)
The path must remain unchanged for the life of this object.
Definition: udisplay.cpp:160
static mouse_handler * get_singleton()
void set_facing(map_location::DIRECTION dir) const
Definition: unit.cpp:1491
GLsizei const char ** path
Definition: glew.h:4654
const map_location & mouseover_hex() const
Definition: display.hpp:293
bool null() const
Definition: variable.hpp:66
GLuint64EXT * result
Definition: glew.h:10727
bool tiles_adjacent(const map_location &a, const map_location &b)
Function which tells if two locations are adjacent.
Definition: location.hpp:314
void finish(unit_ptr u, map_location::DIRECTION dir=map_location::NDIRECTIONS)
Finishes the display of movement for the supplied unit.
Definition: udisplay.cpp:414
void restart_animation()
Definition: animation.cpp:1444
int get_end_time() const
Definition: animation.cpp:1424
pointer get_shared_ptr() const
Definition: map.hpp:180
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:315
const bool force_scroll_
Definition: udisplay.hpp:65
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 Uint32 text_color=0, const unit_animation::hit_type hit_type=unit_animation::INVALID, const attack_type *attack=nullptr, const attack_type *second_attack=nullptr, int value2=0)
Definition: animation.cpp:1315
unit_animator animator_
Definition: udisplay.hpp:66
child_list get_children(const std::string &key) const
Definition: variable.cpp:181
unit_const_ptr first_match_on_map() const
Definition: filter.hpp:98
void update_shown_unit()
Switches the display back to *shown_unit_ after animating.
Definition: udisplay.cpp:229
filter_context * filter_con
Definition: resources.cpp:23
GLboolean GLboolean GLboolean GLboolean a
Definition: glew.h:7319
fake_unit_manager * fake_units
Definition: resources.cpp:32
A class to encapsulate the steps of drawing a unit's move.
Definition: udisplay.hpp:47
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:731
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:387
unit_animation_component & anim_comp() const
Definition: unit.hpp:276
static const map_location & null_location()
Definition: location.hpp:195
int viewing_side() const
Definition: display.hpp:103
void unit_draw_weapon(const map_location &loc, unit &attacker, const attack_type *attack, const attack_type *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:500
void pump()
Definition: events.cpp:336
const unit_map & get_units() const
Definition: display.hpp:121
Encapsulates the map of the game.
Definition: location.hpp:38
void unit_die(const map_location &loc, unit &loser, const attack_type *attack, const attack_type *secondary_attack, const map_location &winner_loc, unit *winner)
Show a unit fading out.
Definition: udisplay.cpp:548
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:203
void get_locations(std::set< map_location > &locs, bool with_border=false) const
Definition: filter.cpp:495
std::map< std::string, tfilter >::iterator itor
Definition: filter.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:2434
static Uint32 rgb(Uint8 red, Uint8 green, Uint8 blue)
Definition: display.hpp:176
bool update_locked() const
Definition: video.cpp:517
void set_location(const map_location &loc)
To be called by unit_map or for temporary units only.
Definition: unit.hpp:288
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:2446
std::pair< const config *, map_location > unit_ability
The things contained within a unit_ability_list.
Definition: unit.hpp:43
const config & get_config() const
Definition: variable.hpp:68
void pause_animation()
Definition: animation.cpp:1435
bool empty() const
Definition: variable.hpp:93
unit_map::iterator find_unit(const map_location &loc)
Definition: game_board.hpp:149
void start(unit_ptr u)
Initiates the display of movement for the supplied unit.
Definition: udisplay.cpp:244
#define log_scope(description)
Definition: log.hpp:185
GLfloat GLfloat GLfloat GLfloat h
Definition: glew.h:5910
DIRECTION
Valid directions which can be moved in our hexagonal world.
Definition: location.hpp:40
void wml_animation_internal(unit_animator &animator, const vconfig &cfg, const map_location &default_location=map_location::null_location())
Definition: udisplay.cpp:782
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 Uint32 text_color=0)
Definition: animation.cpp:1294
unit_ability_list get_abilities(const std::string &tag_name, const map_location &loc) const
Definition: abilities.cpp:168
void reset_helpers(const unit *attacker, const unit *defender)
Definition: udisplay.cpp:680
GLsizeiptr size
Definition: glew.h:1649
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition: glew.h:3448
void unit_sheath_weapon(const map_location &primary_loc, unit *primary_unit, const attack_type *primary_attack, const attack_type *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:518
bool faked() const
Definition: video.hpp:166
boost::intrusive_ptr< unit > unit_ptr
Definition: ptr.hpp:29
void set_all_standing()
Definition: animation.cpp:1453
void unit_recruited(const map_location &loc, const map_location &leader_loc)
Definition: udisplay.cpp:703
display & disp_
Definition: dialogs.cpp:98
A variable-expanding proxy for the config class.
Definition: variable.hpp:36
Standard logging facilities (interface).
fake_unit_ptr temp_unit_ptr_
Definition: udisplay.hpp:71
CVideo & video()
Gets the underlying screen object.
Definition: display.hpp:202
Container associating units to locations.
Definition: map.hpp:90
void set_standing(bool with_bars=true)
Sets the animation state to standing.
unit_iterator find(size_t id)
Definition: map.cpp:285
bool valid() const
Definition: map.hpp:229
std::vector< vconfig > child_list
Definition: variable.hpp:71
int get_animation_time() const
Definition: animation.cpp:1416
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
GLsizei const GLcharARB ** string
Definition: glew.h:4503
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.
unit_map * units
Definition: resources.cpp:35
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:486
void wait_for_end() const
Definition: animation.cpp:1399
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