The Battle for Wesnoth  1.19.11+dev
animation_component.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2025
3  by Chris Beck <render787@gmail.com>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
17 
18 #include "config.hpp"
19 #include "log.hpp"
21 #include "random.hpp"
22 #include "units/unit.hpp"
23 #include "units/types.hpp"
24 
25 #include <set>
26 
27 static lg::log_domain log_engine("engine");
28 #define ERR_NG LOG_STREAM(err, log_engine)
29 
30 using namespace std::chrono_literals;
31 
32 namespace
33 {
34 std::chrono::steady_clock::time_point get_next_idle_tick()
35 {
36  if(!prefs::get().idle_anim()) {
37  return std::chrono::steady_clock::time_point::max();
38  }
39 
40  const double rate = std::pow(2.0, -prefs::get().idle_anim_rate() / 10.0);
41  const int duration = randomness::rng::default_instance().get_random_int(20000, 39999) * rate;
42  return get_current_animation_tick() + std::chrono::milliseconds{duration};
43 }
44 } // namespace
45 
47  const map_location& second_loc,const int value,const strike_result::type hit,
48  const const_attack_ptr& attack, const const_attack_ptr& second_attack, int swing_num)
49 {
50  // Select one of the matching animations at random
51  std::vector<const unit_animation*> options;
52  int max_val = unit_animation::MATCH_FAIL;
53  for(const unit_animation& anim : animations_) {
54  int matching = anim.matches(loc,second_loc,u_.shared_from_this(),event,value,hit,attack,second_attack,swing_num);
55  if(matching > unit_animation::MATCH_FAIL && matching == max_val) {
56  options.push_back(&anim);
57  } else if(matching > max_val) {
58  max_val = matching;
59  options.clear();
60  options.push_back(&anim);
61  }
62  }
63 
64  if(max_val == unit_animation::MATCH_FAIL) {
65  return nullptr;
66  }
67  return options[randomness::rng::default_instance().get_random_int(0, options.size()-1)];
68 }
69 
71 {
72  if (prefs::get().show_standing_animations()&& !u_.incapacitated()) {
73  start_animation(std::chrono::milliseconds::max(), choose_animation(u_.loc_, "standing"),
74  with_bars, "", {0,0,0}, STATE_STANDING);
75  } else {
76  start_animation(std::chrono::milliseconds::max(), choose_animation(u_.loc_, "_disabled_"),
77  with_bars, "", {0,0,0}, STATE_STANDING);
78  }
79 }
80 
82 {
83  start_animation(std::chrono::milliseconds::max(), choose_animation(u_.loc_, "_ghosted_"),
84  with_bars);
85  anim_->pause_animation();
86 }
87 
89 {
90  start_animation(std::chrono::milliseconds::max(), choose_animation(u_.loc_, "_disabled_ghosted_"),
91  with_bars);
92 }
93 
95 {
96  start_animation(std::chrono::milliseconds::max(), choose_animation(u_.loc_, "idling"),
97  true, "", {0,0,0}, STATE_FORGET);
98 }
99 
101 {
102  if (prefs::get().show_standing_animations() && !u_.incapacitated()) {
103  start_animation(std::chrono::milliseconds::max(), choose_animation(u_.loc_, "selected"),
104  true, "", {0,0,0}, STATE_FORGET);
105  } else {
106  start_animation(std::chrono::milliseconds::max(), choose_animation(u_.loc_, "_disabled_selected_"),
107  true, "", {0,0,0}, STATE_FORGET);
108  }
109 }
110 
111 void unit_animation_component::start_animation(const std::chrono::milliseconds& start_time, const unit_animation *animation,
112  bool with_bars, const std::string &text, color_t text_color, STATE state)
113 {
114  if (!animation) {
115  if (state == STATE_STANDING)
116  state_ = state;
117  if (!anim_ && state_ != STATE_STANDING)
118  set_standing(with_bars);
119  return ;
120  }
121  state_ = state;
122  // everything except standing select and idle
123  bool accelerate = (state != STATE_FORGET && state != STATE_STANDING);
124  draw_bars_ = with_bars;
125  anim_.reset(new unit_animation(*animation));
126  const auto real_start_time = start_time == std::chrono::milliseconds::max() ? anim_->get_begin_time() : start_time;
127  anim_->start_animation(real_start_time, u_.loc_, u_.loc_.get_direction(u_.facing_),
128  text, text_color, accelerate);
129  frame_begin_time_ = anim_->get_begin_time() - 1ms;
130  next_idling_ = get_next_idle_tick();
131 }
132 
134 {
135  if (state_ == STATE_FORGET && anim_ && anim_->animation_finished_potential())
136  {
137  set_standing();
138  return;
139  }
140  display &disp = *display::get_singleton();
141  if (state_ != STATE_STANDING || get_current_animation_tick() < next_idling_ ||
142  !disp.tile_nearly_on_screen(u_.loc_) || u_.incapacitated())
143  {
144  return;
145  }
146  if (get_current_animation_tick() > next_idling_ + 1000ms)
147  {
148  // prevent all units animating at the same time
149  next_idling_ = get_next_idle_tick();
150  } else {
151  set_idling();
152  }
153 }
154 
156 {
157  unit_halo_.reset();
158  abil_halos_.clear();
159  abil_halos_ref_.clear();
160  if(anim_ ) anim_->clear_haloes();
161 }
162 
164 {
165  bool result = false;
166 
167  // Very early calls, anim not initialized yet
168  if(get_animation()) {
169  frame_parameters params;
170  const gamemap& map = disp.context().map();
171  const t_translation::terrain_code terrain = map.get_terrain(u_.loc_);
172  const terrain_type& terrain_info = map.get_terrain_info(terrain);
173 
174  int height_adjust = static_cast<int>(terrain_info.unit_height_adjust() * disp.get_zoom_factor());
175  if (u_.is_flying() && height_adjust < 0) {
176  height_adjust = 0;
177  }
178  params.y -= height_adjust;
179  params.halo_y -= height_adjust;
180  params.image_mod = u_.image_mods();
181  params.halo_mod = u_.TC_image_mods();
182  params.image= u_.default_anim_image();
183 
184  result |= get_animation()->invalidate(params);
185  }
186 
187  return result;
188 
189 }
190 
192 {
193  if (newtype) {
194  animations_ = newtype->animations();
195  }
196 
197  refreshing_ = false;
198  anim_.reset();
199 }
200 
202 {
203  bool affect_adjacent = false;
204  for(const auto [key, cfg] : u_.abilities().all_children_view()) {
205  bool image_or_hides = (key == "hides" || cfg.has_attribute("halo_image") || cfg.has_attribute("overlay_image"));
206  if(image_or_hides && cfg.has_child("affect_adjacent")){
207  affect_adjacent = true;
208  break;
209  }
210  }
211  if(affect_adjacent) {
212  const auto adjacent = get_adjacent_tiles(u_.get_location());
213  for(unsigned i = 0; i < adjacent.size(); ++i) {
214  const unit_map::const_iterator it = units.find(adjacent[i]);
215  if (it == units.end() || it->incapacitated()){
216  continue;
217  }
218  if ( &*it == &u_ ){
219  ERR_NG << "Impossible situation: the unit is adjacent to itself.";
220  continue;
221  }
222  it->anim_comp().set_standing();
223  }
224  }
225 }
226 
228  if(effect["id"].empty()) {
229  unit_animation::add_anims(animations_, effect);
230  } else {
231  static std::map< std::string, std::vector<unit_animation>> animation_cache;
232  std::vector<unit_animation> &built = animation_cache[effect["id"]];
233  if(built.empty()) {
234  unit_animation::add_anims(built, effect);
235  }
236  animations_.insert(animations_.end(),built.begin(),built.end());
237  }
238 }
239 
240 std::vector<std::string> unit_animation_component::get_flags() {
241  std::set<std::string> result;
242  for(const auto& anim : animations_) {
243  const std::vector<std::string>& flags = anim.get_flags();
244  std::copy_if(flags.begin(), flags.end(), std::inserter(result, result.begin()), [](const std::string& flag) {
245  return !(flag.empty() || (flag.front() == '_' && flag.back() == '_'));
246  });
247  }
248  return std::vector<std::string>(result.begin(), result.end());
249 }
map_location loc
Definition: move.cpp:172
std::chrono::steady_clock::time_point get_current_animation_tick()
Definition: animated.cpp:34
static lg::log_domain log_engine("engine")
#define ERR_NG
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:158
virtual const gamemap & map() const =0
Sort-of-Singleton that many classes, both GUI and non-GUI, use to access the game data.
Definition: display.hpp:96
static double get_zoom_factor()
Returns the current zoom factor.
Definition: display.hpp:269
bool tile_nearly_on_screen(const map_location &loc) const
Checks if location loc or one of the adjacent tiles is visible on screen.
Definition: display.cpp:1786
const display_context & context() const
Definition: display.hpp:192
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:110
terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition: map.cpp:270
Encapsulates the map of the game.
Definition: map.hpp:172
const terrain_type & get_terrain_info(const t_translation::terrain_code &terrain) const
Definition: map.cpp:97
static prefs & get()
static rng & default_instance()
Definition: random.cpp:73
int get_random_int(int min, int max)
Definition: random.hpp:51
int unit_height_adjust() const
Definition: terrain.hpp:140
void set_idling()
Sets the animation state to idling.
void apply_new_animation_effect(const config &effect)
Adds an animation described by a config.
bool invalidate(const display &disp)
Invalidates an animation with respect to a display object, preparing it for redraw.
void set_ghosted(bool with_bars=true)
Sets the animation state to ghosted.
void refresh()
Intermittently activates the idling animations in place of the standing animations.
void reset_after_advance(const unit_type *newtype=nullptr)
Resets the animations list after the unit is advanced.
void reset_affect_adjacent(const unit_map &units)
Refresh map around unit if has ability with [affect_adjacent/distant] tag.
std::vector< std::string > get_flags()
Get the flags of all registered animations.
void set_standing(bool with_bars=true)
Sets the animation state to standing.
STATE
States for animation.
void set_selecting()
Sets the animation state to that when the unit is selected.
void clear_haloes()
Clear the haloes associated to the unit.
void set_disabled_ghosted(bool with_bars=true)
Whiteboard related somehow.
const unit_animation * choose_animation(const map_location &loc, const std::string &event, const map_location &second_loc=map_location::null_location(), const int damage=0, const strike_result::type hit_type=strike_result::type::invalid, const const_attack_ptr &attack=nullptr, const const_attack_ptr &second_attack=nullptr, int swing_num=0)
Chooses an appropriate animation from the list of known animations.
void start_animation(const std::chrono::milliseconds &start_time, const unit_animation *animation, bool with_bars, const std::string &text="", color_t text_color={}, STATE state=STATE_ANIM)
Begin an animation.
static void add_anims(std::vector< unit_animation > &animations, const config &cfg)
Definition: animation.cpp:628
Container associating units to locations.
Definition: map.hpp:98
unit_iterator end()
Definition: map.hpp:428
unit_iterator find(std::size_t id)
Definition: map.cpp:302
A single unit type that the player may recruit.
Definition: types.hpp:43
const std::vector< unit_animation > & animations() const
Definition: types.cpp:536
Definitions for the interface to Wesnoth Markup Language (WML).
std::size_t i
Definition: function.cpp:1030
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.cpp:479
Standard logging facilities (interface).
std::shared_ptr< const attack_type > const_attack_ptr
Definition: ptr.hpp:34
The basic class for representing 8-bit RGB or RGBA colour values.
Definition: color.hpp:61
All parameters from a frame at a given instant.
Definition: frame.hpp:44
std::string image_mod
Definition: frame.hpp:50
image::locator image
Definition: frame.hpp:47
std::string halo_mod
Definition: frame.hpp:56
Encapsulates the map of the game.
Definition: location.hpp:45
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:49