The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
heal.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 /**
16  * @file
17  * Healing (at start of side turn).
18  */
19 
20 #include "heal.hpp"
21 
22 #include "game_board.hpp"
23 #include "game_display.hpp"
24 #include "gettext.hpp"
25 #include "log.hpp"
26 #include "map/map.hpp"
27 #include "play_controller.hpp"
28 #include "resources.hpp"
29 #include "team.hpp"
30 #include "units/unit.hpp"
31 #include "units/abilities.hpp"
32 #include "units/udisplay.hpp"
33 #include "units/map.hpp"
34 
35 #include <list>
36 #include <vector>
37 
38 static lg::log_domain log_engine("engine");
39 #define DBG_NG LOG_STREAM(debug, log_engine)
40 
41 
42 namespace {
43 
44  // Contains the data needed to display healing.
45  struct heal_unit {
46  heal_unit(unit &patient, const std::vector<unit *> &treaters, int healing,
47  bool poison) :
48  healed(patient),
49  healers(treaters),
50  amount(healing),
51  cure_poison(poison)
52  {}
53 
54  unit & healed;
55  std::vector<unit *> healers;
56  int amount;
57  bool cure_poison;
58  };
59 
60  // Keep these ordered from weakest cure to strongest cure.
61  enum POISON_STATUS { POISON_NORMAL, POISON_SLOW , POISON_CURE };
62 
63 
64  /**
65  * Converts a string into its corresponding POISON_STATUS.
66  */
67  POISON_STATUS poison_status(const std::string & status)
68  {
69  if ( status == "cured" )
70  return POISON_CURE;
71 
72  if ( status == "slowed" )
73  return POISON_SLOW;
74 
75  // No other states recognized.
76  return POISON_NORMAL;
77  }
78 
79 
80  /**
81  * Determines if @a patient is affected by anything that impacts poison.
82  * If cured by a unit, that unit is added to @a healers.
83  */
84  POISON_STATUS poison_progress(int side, const unit & patient,
85  std::vector<unit *> & healers)
86  {
87  const std::vector<team> &teams = *resources::teams;
89 
90  POISON_STATUS curing = POISON_NORMAL;
91 
92 
93  if ( patient.side() == side )
94  {
95  // Village healing?
96  if ( resources::gameboard->map().gives_healing(patient.get_location()) )
97  return POISON_CURE;
98 
99  // Regeneration?
100  for (const unit_ability & regen : patient.get_abilities("regenerate"))
101  {
102  curing = std::max(curing, poison_status((*regen.first)["poison"]));
103  if ( curing == POISON_CURE )
104  // This is as good as it gets.
105  return POISON_CURE;
106  }
107  }
108 
109  // Look through the healers to find a curer.
110  unit_map::iterator curer = units.end();
111  // Assumed: curing is not POISON_CURE at the start of any iteration.
112  for (const unit_ability & heal : patient.get_abilities("heals"))
113  {
114  POISON_STATUS this_cure = poison_status((*heal.first)["poison"]);
115  if ( this_cure <= curing )
116  // We already recorded this level of curing.
117  continue;
118 
119  // NOTE: At this point, this_cure will be *_SLOW or *_CURE.
120 
121  unit_map::iterator cure_it = units.find(heal.second);
122  assert(cure_it != units.end());
123  const int cure_side = cure_it->side();
124 
125  // Healers on the current side can cure poison (for any side).
126  // Allies of the current side can slow poison (for the current side).
127  // Enemies of the current side can do nothing.
128  if ( teams[cure_side-1].is_enemy(side) )
129  continue;
130 
131  // Allied healers can only slow poison, not cure it.
132  if ( cure_side != side )
133  this_cure = POISON_SLOW;
134  // This is where the loop assumption comes into play,
135  // as we do not bother comparing POISON_SLOW to curing.
136 
137  if ( this_cure == POISON_CURE ) {
138  // Return what we found.
139  healers.push_back(&*cure_it);
140  return POISON_CURE;
141  }
142 
143  // Record this potential treatment.
144  curer = cure_it;
145  curing = this_cure;
146  }
147 
148  // Return the best curing we found.
149  if ( curer != units.end() )
150  healers.push_back(&*curer);
151  return curing;
152  }
153 
154 
155  /**
156  * Updates the current healing and harming amounts based on a new value.
157  * This is a helper function for heal_amount().
158  * @returns true if an amount was updated.
159  */
160  inline bool update_healing(int & healing, int & harming, int value)
161  {
162  // If the new value magnifies the healing, update and return true.
163  if ( value > healing ) {
164  healing = value;
165  return true;
166  }
167 
168  // If the new value magnifies the harming, update and return true.
169  if ( value < harming ) {
170  harming = value;
171  return true;
172  }
173 
174  // Keeping the same values as before.
175  return false;
176  }
177  /**
178  * Calculate how much @patient heals this turn.
179  * If healed by units, those units are added to @a healers.
180  */
181  int heal_amount(int side, const unit & patient, std::vector<unit *> & healers)
182  {
183  unit_map &units = *resources::units;
184 
185  int healing = 0;
186  int harming = 0;
187 
188 
189  if ( patient.side() == side )
190  {
191  // Village healing?
192  update_healing(healing, harming,
193  resources::gameboard->map().gives_healing(patient.get_location()));
194 
195  // Regeneration?
196  unit_ability_list regen_list = patient.get_abilities("regenerate");
197  unit_abilities::effect regen_effect(regen_list, 0, false);
198  update_healing(healing, harming, regen_effect.get_composite_value());
199  }
200 
201  // Check healing from other units.
202  unit_ability_list heal_list = patient.get_abilities("heals");
203  // Remove all healers not on this side (since they do not heal now).
204  unit_ability_list::iterator heal_it = heal_list.begin();
205  while ( heal_it != heal_list.end() ) {
206  unit_map::iterator healer = units.find(heal_it->second);
207  assert(healer != units.end());
208 
209  if ( healer->side() != side )
210  heal_it = heal_list.erase(heal_it);
211  else
212  ++heal_it;
213  }
214 
215  // Now we can get the aggregate healing amount.
216  unit_abilities::effect heal_effect(heal_list, 0, false);
217  if ( update_healing(healing, harming, heal_effect.get_composite_value()) )
218  {
219  // Collect the healers involved.
220  for (const unit_abilities::individual_effect & heal : heal_effect)
221  healers.push_back(&*units.find(heal.loc));
222 
223  if ( !healers.empty() ) {
224  DBG_NG << "Unit has " << healers.size() << " healers.\n";
225  }
226  }
227 
228  return healing + harming;
229  }
230 
231 
232  /**
233  * Handles the actual healing.
234  */
235  void do_heal(unit &patient, int amount, bool cure_poison)
236  {
237  if ( cure_poison )
238  patient.set_state(unit::STATE_POISONED, false);
239  if ( amount > 0)
240  patient.heal(amount);
241  else if ( amount < 0 )
242  patient.take_hit(-amount);
244  }
245 
246 
247  /**
248  * Animates the healings in the provided list.
249  * (The list will be empty when this returns.)
250  */
251  void animate_heals(std::list<heal_unit> &unit_list)
252  {
253  // Use a nearest-first algorithm.
254  map_location last_loc(0,-1);
255  while ( !unit_list.empty() )
256  {
258  int min_dist = INT_MAX;
259 
260  // Next unit to be healed is the entry in list nearest to last_loc.
261  for ( std::list<heal_unit>::iterator check_it = unit_list.begin();
262  check_it != unit_list.end(); ++check_it )
263  {
264  int distance = distance_between(last_loc, check_it->healed.get_location());
265  if ( distance < min_dist ) {
266  min_dist = distance;
267  nearest = check_it;
268  // Allow an early exit if we cannot get closer.
269  if ( distance == 1 )
270  break;
271  }
272  }
273 
274  std::string cure_text = "";
275  if ( nearest->cure_poison )
276  cure_text = nearest->healed.gender() == unit_race::FEMALE ?
277  _("female^cured") : _("cured");
278 
279  // The heal (animated, then actual):
280  unit_display::unit_healing(nearest->healed, nearest->healers,
281  nearest->amount, cure_text);
282  do_heal(nearest->healed, nearest->amount, nearest->cure_poison);
283 
284  // Update the loop variables.
285  last_loc = nearest->healed.get_location();
286  unit_list.erase(nearest);
287  }
288  }
289 
290 }//anonymous namespace
291 
292 
293 // Simple algorithm: no maximum number of patients per healer.
294 void calculate_healing(int side, bool update_display)
295 {
296  DBG_NG << "beginning of healing calculations\n";
297 
298  std::list<heal_unit> unit_list;
299 
300  // We look for all allied units, then we see if our healer is near them.
301  for (unit &patient : *resources::units) {
302 
303  if ( patient.get_state("unhealable") || patient.incapacitated() ) {
304  if ( patient.side() == side )
305  patient.set_resting(true);
306  continue;
307  }
308 
309  DBG_NG << "found healable unit at (" << patient.get_location() << ")\n";
310 
311  POISON_STATUS curing = POISON_NORMAL;
312  int healing = 0;
313  std::vector<unit *> healers;
314 
315 
316  // Rest healing.
317  if ( patient.side() == side ) {
318  if ( patient.resting() || patient.is_healthy() )
320  patient.set_resting(true);
321  }
322 
323  // Main healing.
324  if ( !patient.get_state(unit::STATE_POISONED) ) {
325  healing += heal_amount(side, patient, healers);
326  }
327  else {
328  curing = poison_progress(side, patient, healers);
329  // Poison can be cured at any time, but damage is only
330  // taken on the patient's turn.
331  if ( curing == POISON_NORMAL && patient.side() == side )
332  healing -= game_config::poison_amount;
333  }
334 
335  // Cap the healing.
336  int max_heal = std::max(0, patient.max_hitpoints() - patient.hitpoints());
337  int min_heal = std::min(0, 1 - patient.hitpoints());
338  if ( healing < min_heal )
339  healing = min_heal;
340  else if ( healing > max_heal )
341  healing = max_heal;
342 
343  // Is there nothing to do?
344  if ( curing != POISON_CURE && healing == 0 )
345  continue;
346 
347  if (!healers.empty()) {
348  DBG_NG << "Just before healing animations, unit has " << healers.size() << " potential healers.\n";
349  }
350 
351  const team & viewing_team =
352  (*resources::teams)[resources::screen->viewing_team()];
353  if (!resources::controller->is_skipping_replay() && update_display &&
354  patient.is_visible_to_team(viewing_team, resources::gameboard->map(), false) )
355  {
356  unit_list.push_front(heal_unit(patient, healers, healing, curing == POISON_CURE));
357  }
358  else
359  {
360  // Do the healing now since it will not be animated.
361  do_heal(patient, healing, curing == POISON_CURE);
362  }
363  }
364 
365  animate_heals(unit_list);
366 
367  DBG_NG << "end of healing calculations\n";
368 }
369 
void set_resting(bool rest)
Definition: unit.hpp:243
play_controller * controller
Definition: resources.cpp:21
void heal(int amount)
Definition: unit.cpp:1253
bool is_visible_to_team(team const &team, gamemap const &map, bool const see_all=true) const
Definition: unit.cpp:2333
static lg::log_domain log_engine("engine")
unit_iterator end()
Definition: map.hpp:311
const map_location & get_location() const
Definition: unit.hpp:286
int max_hitpoints() const
Definition: unit.hpp:169
bool get_state(const std::string &state) const
Definition: unit.cpp:1289
Definition: unit.hpp:95
iterator erase(const iterator &erase_it)
Definition: unit.hpp:86
int hitpoints() const
Definition: unit.hpp:168
game_display * screen
Definition: resources.cpp:27
bool is_healthy() const
Definition: unit.hpp:307
void invalidate_unit()
Function to invalidate that unit status displayed on the sidebar.
int side() const
Definition: unit.hpp:201
void set_state(const std::string &state, bool value)
Definition: unit.cpp:1337
-file sdl_utils.hpp
int rest_heal_amount
Definition: game_config.cpp:41
std::vector< unit_ability >::iterator iterator
Definition: unit.hpp:72
static std::vector< team > *& teams
Definition: team.cpp:50
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:50
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:82
bool incapacitated() const
Definition: unit.hpp:215
std::vector< team > * teams
Definition: resources.cpp:29
size_t distance_between(const map_location &a, const map_location &b)
Function which gives the number of hexes between two tiles (i.e.
Definition: location.hpp:357
POISON_STATUS
Definition: heal.cpp:61
GLsizei const GLfloat * value
Definition: glew.h:1817
game_board * gameboard
Definition: resources.cpp:20
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::string &parameters float amount
Definition: filter.cpp:132
Encapsulates the map of the game.
Definition: location.hpp:38
void calculate_healing(int side, bool update_display)
Calculates healing for all units for the given side.
Definition: heal.cpp:294
std::pair< const config *, map_location > unit_ability
The things contained within a unit_ability_list.
Definition: unit.hpp:43
iterator begin()
Definition: unit.hpp:74
bool resting() const
Definition: unit.hpp:242
bool take_hit(int damage)
Called on every draw.
Definition: unit.hpp:239
unit_ability_list get_abilities(const std::string &tag_name, const map_location &loc) const
Definition: abilities.cpp:168
virtual const gamemap & map() const
Definition: game_board.hpp:98
Various functions that implement healing of units (when a side turn starts).
Standard logging facilities (interface).
Container associating units to locations.
Definition: map.hpp:90
size_t viewing_team() const
The viewing team is the team currently viewing the game.
Definition: display.hpp:102
unit_iterator find(size_t id)
Definition: map.cpp:285
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
GLsizei const GLcharARB ** string
Definition: glew.h:4503
unit_map * units
Definition: resources.cpp:35
Display units performing various actions: moving, attacking, and dying.
iterator end()
Definition: unit.hpp:76
#define DBG_NG
Definition: heal.cpp:39