The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
unit.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  * Routines to manage units.
18  */
19 
20 #include "units/unit.hpp"
21 #include "global.hpp"
22 
23 #include "display_context.hpp"
24 #include "formula/string_utils.hpp" // for vgettext
25 #include "game_board.hpp" // for game_board
26 #include "game_data.hpp"
27 #include "game_config.hpp" // for add_color_info, etc
28 #include "game_errors.hpp" // for game_error
29 #include "game_events/manager.hpp" // for add_events
30 #include "game_preferences.hpp" // for encountered_units
31 #include "gettext.hpp" // for N_
32 #include "log.hpp" // for LOG_STREAM, logger, etc
33 #include "utils/make_enum.hpp" // for operator<<, operator>>
34 #include "map/map.hpp" // for gamemap
35 #include "random_new.hpp" // for generator, rng
36 #include "resources.hpp" // for units, gameboard, teams, etc
37 #include "scripting/game_lua_kernel.hpp" // for game_lua_kernel
38 #include "synced_context.hpp"
39 #include "side_filter.hpp" // for side_filter
40 #include "team.hpp" // for team, get_teams, etc
41 #include "terrain/filter.hpp" // for terrain_filter
42 #include "units/abilities.hpp" // for effect, filter_base_matches
43 #include "units/animation.hpp" // for unit_animation
44 #include "units/animation_component.hpp" // for unit_animation_component
45 #include "units/filter.hpp"
46 #include "units/formula_manager.hpp" // for unit_formula_manager
47 #include "units/id.hpp"
48 #include "units/map.hpp" // for unit_map, etc
49 #include "variable.hpp" // for vconfig, etc
50 
51 #include "utils/functional.hpp"
52 #include <boost/intrusive_ptr.hpp> // for intrusive_ptr
53 #include <boost/function_output_iterator.hpp>
54 #include <boost/range/begin.hpp>
55 #include <boost/range/end.hpp>
56 
57 #ifdef _MSC_VER
58 #pragma warning (push)
59 #pragma warning (disable: 4510 4610)
60 #endif
61 #include <boost/range/algorithm.hpp>
62 #ifdef _MSC_VER
63 #pragma warning (pop)
64 #endif
65 #include <cassert> // for assert
66 #include <cstdlib> // for rand
67 #include <exception> // for exception
68 #include <iterator> // for back_insert_iterator, etc
69 #include <new> // for operator new
70 #include <ostream> // for operator<<, basic_ostream, etc
71 #include <SDL_video.h> // for SDL_Color
72 
73 
74 namespace t_translation { struct t_terrain; }
75 
76 static lg::log_domain log_unit("unit");
77 #define DBG_UT LOG_STREAM(debug, log_unit)
78 #define LOG_UT LOG_STREAM(info, log_unit)
79 #define WRN_UT LOG_STREAM(warn, log_unit)
80 #define ERR_UT LOG_STREAM(err, log_unit)
81 
82 static lg::log_domain log_engine("engine");
83 #define ERR_NG LOG_STREAM(err, log_engine)
84 
85 static lg::log_domain log_config("config");
86 #define WRN_CF LOG_STREAM(warn, log_config)
87 #define ERR_CONFIG LOG_STREAM(err, log_config)
88 
89 static lg::log_domain log_enginerefac("enginerefac");
90 #define LOG_RG LOG_STREAM(info, log_enginerefac)
91 
92 namespace {
93  // "advance" only kept around for backwards compatibility; only "advancement" should be used
94  const std::string ModificationTypes[] = { "advancement", "advance", "trait", "object" };
95  const size_t NumModificationTypes = sizeof(ModificationTypes)/
96  sizeof(*ModificationTypes);
97 
98  /**
99  * Pointers to units which have data in their internal caches. The
100  * destructor of an unit removes itself from the cache, so the pointers are
101  * always valid.
102  */
103  static std::vector<const unit *> units_with_cache;
104 
105  const std::string leader_crown_path = "misc/leader-crown.png";
106  static std::string internalized_attrs[] = { "type", "id", "name",
107  "gender", "random_gender", "variation", "role", "ai_special",
108  "side", "underlying_id", "overlays", "facing", "race",
109  "level", "recall_cost", "undead_variation", "max_attacks",
110  "attacks_left", "alpha", "zoc", "flying", "cost",
111  "max_hitpoints", "max_moves", "vision", "jamming", "max_experience",
112  "advances_to", "hitpoints", "goto_x", "goto_y", "moves",
113  "experience", "resting", "unrenamable", "alignment",
114  "canrecruit", "extra_recruit", "x", "y", "placement",
115  "parent_type", "description", "usage", "halo", "ellipse",
116  "random_taits", "upkeep", "random_traits", "generate_name",
117  "profile", "small_profile",
118  // Useless attributes created when saving units to WML:
119  "flag_rgb", "language_name", "image", "image_icon"
120  };
121  //Sort the array to make set_difference below work.
122  struct t_internalized_attrs_sorter {
123  t_internalized_attrs_sorter()
124  {
125  std::sort(boost::begin(internalized_attrs), boost::end(internalized_attrs));
126  }
127  } internalized_attrs_sorter;
128 
129  void warn_unknown_attribute(const config::const_attr_itors& cfg)
130  {
131  config::const_attribute_iterator cur = cfg.first;
133  const std::string* cur_known = boost::begin(internalized_attrs);
134  const std::string* end_known = boost::end(internalized_attrs);
135  while(cur_known != end_known) {
136  if(cur == end) {
137  return;
138  }
139  int comp = cur->first.compare(*cur_known);
140  if(comp < 0) {
141  WRN_UT << "Unknown attribute '" << cur->first << "' discarded." << std::endl;
142  ++cur;
143  }
144  else if (comp == 0) {
145  ++cur;
146  ++cur_known;
147  }
148  else {
149  ++cur_known;
150  }
151  }
152  while(cur != end) {
153  WRN_UT << "Unknown attribute '" << cur->first << "' discarded." << std::endl;
154  ++cur;
155  }
156  }
157 }
158 
159 /**
160  * Intrusive Pointer interface
161  *
162  **/
163 
165 {
166  assert(u->ref_count_ >= 0);
167  // the next code line is to notice possible wrongly initialized units.
168  // The 100000 is picked rather randomly. If you are in the situation
169  // that you can actually have more then 100000 intrusive_ptr to one unit
170  // or if you are sure that the refcounting system works
171  // then feel free to remove the next line
172  assert(u->ref_count_ < 100000);
173  LOG_UT << "Adding a reference to a unit: id = " << u->id() << ", uid = " << u->underlying_id() << ", refcount = " << u->ref_count() << " ptr:" << u << std::endl;
174  if (u->ref_count_ == 0) {
175  LOG_UT << "Freshly constructed" << std::endl;
176  }
177  ++(u->ref_count_);
178 }
179 
181 {
182  assert(u->ref_count_ >= 1);
183  assert(u->ref_count_ < 100000); //See comment in intrusive_ptr_add_ref
184  LOG_UT << "Removing a reference to a unit: id = " << u->id() << ", uid = " << u->underlying_id() << ", refcount = " << u->ref_count() << " ptr:" << u << std::endl;
185  if (--(u->ref_count_) == 0)
186  {
187  LOG_UT << "Deleting a unit: id = " << u->id() << ", uid = " << u->underlying_id() << std::endl;
188  delete u;
189  }
190 }
191 
192 /**
193  * Converts a string ID to a unit_type.
194  * Throws a game_error exception if the string does not correspond to a type.
195  */
196 static const unit_type &get_unit_type(const std::string &type_id)
197 {
198  if (type_id.empty()) {
199  throw unit_type::error("creating unit with an empty type field");
200  }
201  std::string new_id = type_id;
202  unit_type::check_id(new_id);
203  const unit_type *i = unit_types.find(new_id);
204  if (!i) throw unit_type::error("unknown unit type: " + type_id);
205  return *i;
206 }
207 
208 static unit_race::GENDER generate_gender(const unit_type & type, bool random_gender)
209 {
210  const std::vector<unit_race::GENDER>& genders = type.genders();
211  assert( genders.size() > 0 );
212 
213  if ( random_gender == false || genders.size() == 1 ) {
214  return genders.front();
215  } else {
216  return genders[random_new::generator->get_random_int(0,genders.size()-1)];
217  // Note: genders is guaranteed to be non-empty, so this is not a
218  // potential division by zero.
219  // Note: Whoever wrote this code, you should have used an assertion, to save others hours of work...
220  // If the assertion size>0 is failing for you, one possible cause is that you are constructing a unit
221  // from a unit type which has not been ``built'' using the unit_type_data methods.
222  }
223 }
224 
225 static unit_race::GENDER generate_gender(const unit_type & u_type, const config &cfg)
226 {
227  const std::string& gender = cfg["gender"];
228  if(!gender.empty())
229  return string_gender(gender);
230 
231  return generate_gender(u_type, cfg["random_gender"].to_bool());
232 }
233 
235 {
236  return leader_crown_path;
237 }
238 namespace {
239  template<typename T>
240  T* copy_or_null(const boost::scoped_ptr<T>& ptr)
241  {
242  return ptr ? new T(*ptr) : nullptr;
243  }
244 }
245 // Copy constructor
246 unit::unit(const unit& o)
247  : ref_count_(0)
248  , loc_(o.loc_)
249  , advances_to_(o.advances_to_)
250  , type_(o.type_)
251  , type_name_(o.type_name_)
252  , race_(o.race_)
253  , id_(o.id_)
254  , name_(o.name_)
255  , underlying_id_(o.underlying_id_)
256  , undead_variation_(o.undead_variation_)
257  , variation_(o.variation_)
258  , hit_points_(o.hit_points_)
259  , max_hit_points_(o.max_hit_points_)
260  , experience_(o.experience_)
261  , max_experience_(o.max_experience_)
262  , level_(o.level_)
263  , recall_cost_(o.recall_cost_)
264  , canrecruit_(o.canrecruit_)
265  , recruit_list_(o.recruit_list_)
266  , alignment_(o.alignment_)
267  , flag_rgb_(o.flag_rgb_)
268  , image_mods_(o.image_mods_)
269  , unrenamable_(o.unrenamable_)
270  , side_(o.side_)
271  , gender_(o.gender_)
272  , alpha_(o.alpha_)
273  , formula_man_(new unit_formula_manager(o.formula_manager()))
274  , movement_(o.movement_)
275  , max_movement_(o.max_movement_)
276  , vision_(o.vision_)
277  , jamming_(o.jamming_)
278  , movement_type_(o.movement_type_)
279  , hold_position_(o.hold_position_)
280  , end_turn_(o.end_turn_)
281  , resting_(o.resting_)
282  , attacks_left_(o.attacks_left_)
283  , max_attacks_(o.max_attacks_)
284  , states_(o.states_)
285  , known_boolean_states_(o.known_boolean_states_)
286  , variables_(o.variables_)
287  , events_(o.events_)
288  , filter_recall_(o.filter_recall_)
289  , emit_zoc_(o.emit_zoc_)
290  , overlays_(o.overlays_)
291  , role_(o.role_)
292  , attacks_(o.attacks_)
293  , facing_(o.facing_)
294  , trait_names_(o.trait_names_)
295  , trait_descriptions_(o.trait_descriptions_)
296  , unit_value_(o.unit_value_)
297  , goto_(o.goto_)
298  , interrupted_move_(o.interrupted_move_)
299  , is_fearless_(o.is_fearless_)
300  , is_healthy_(o.is_healthy_)
301  , modification_descriptions_(o.modification_descriptions_)
302  , anim_comp_(new unit_animation_component(*this, *o.anim_comp_))
303  , getsHit_(o.getsHit_)
304  , hidden_(o.hidden_)
305  , hp_bar_scaling_(o.hp_bar_scaling_)
306  , xp_bar_scaling_(o.xp_bar_scaling_)
307  , modifications_(o.modifications_)
308  , abilities_(o.abilities_)
309  , advancements_(o.advancements_)
310  , description_(o.description_)
311  , usage_(copy_or_null(o.usage_))
312  , halo_(copy_or_null(o.halo_))
313  , ellipse_(copy_or_null(o.ellipse_))
314  , random_traits_(o.random_traits_)
315  , generate_name_(o.generate_name_)
316  , upkeep_(o.upkeep_)
317  , profile_(o.profile_)
318  , small_profile_(o.small_profile_)
319  , invisibility_cache_()
320 {
321 }
322 
324 {
325  ptr_vector_pushback(boost::ptr_vector<config>& vec) : vec_(&vec) {}
326  void operator()(const config& cfg)
327  {
328  vec_->push_back(new config(cfg));
329  }
330  //Dont use reference to be copyable.
331  boost::ptr_vector<config>* vec_;
332 };
333 
334 unit::unit(const config &cfg, bool use_traits, const vconfig* vcfg, n_unit::id_manager* id_manager)
335  : ref_count_(0)
336  , loc_(cfg["x"] - 1, cfg["y"] - 1)
337  , advances_to_()
338  , type_(&get_unit_type(cfg["parent_type"].blank() ? cfg["type"] : cfg["parent_type"]))
339  , type_name_()
340  , race_(&unit_race::null_race)
341  , id_(cfg["id"])
342  , name_(cfg["name"].t_str())
343  , underlying_id_(0)
344  , undead_variation_()
345  , variation_(cfg["variation"].empty() ? type_->default_variation() : cfg["variation"])
346  , hit_points_(1)
347  , max_hit_points_(0)
348  , experience_(0)
349  , max_experience_(0)
350  , level_(0)
351  , recall_cost_(-1)
352  , canrecruit_(cfg["canrecruit"].to_bool())
353  , recruit_list_()
354  , alignment_()
355  , flag_rgb_()
356  , image_mods_()
357  , unrenamable_(false)
358  , side_(0)
359  , gender_(generate_gender(*type_, cfg))
360  , alpha_()
361  , formula_man_(new unit_formula_manager())
362  , movement_(0)
363  , max_movement_(0)
364  , vision_(-1)
365  , jamming_(0)
366  , movement_type_()
367  , hold_position_(false)
368  , end_turn_(false)
369  , resting_(false)
370  , attacks_left_(0)
371  , max_attacks_(0)
372  , states_()
373  , known_boolean_states_(known_boolean_state_names_.size(),false)
374  , variables_()
375  , events_()
376  , filter_recall_()
377  , emit_zoc_(0)
378  , overlays_()
379  , role_(cfg["role"])
380  , attacks_()
381  , facing_(map_location::NDIRECTIONS)
382  , trait_names_()
383  , trait_descriptions_()
384  , unit_value_()
385  , goto_()
386  , interrupted_move_()
387  , is_fearless_(false)
388  , is_healthy_(false)
389  , modification_descriptions_()
390  , anim_comp_(new unit_animation_component(*this))
391  , getsHit_(0)
392  , hidden_(false)
393  , hp_bar_scaling_(cfg["hp_bar_scaling"].blank() ? type_->hp_bar_scaling() : cfg["hp_bar_scaling"])
394  , xp_bar_scaling_(cfg["xp_bar_scaling"].blank() ? type_->xp_bar_scaling() : cfg["xp_bar_scaling"])
395  , modifications_()
396  , abilities_()
397  , advancements_()
398  , description_()
399  , usage_()
400  , halo_()
401  , ellipse_()
402  , random_traits_(true)
403  , generate_name_(true)
404  , upkeep_()
405  , invisibility_cache_()
406 {
407  side_ = cfg["side"];
408  if(side_ <= 0) {
409  side_ = 1;
410  }
411 
413  underlying_id_ = n_unit::unit_id::create_real(cfg["underlying_id"].to_int());
414  set_underlying_id(id_manager ? *id_manager : resources::gameboard->unit_id_manager());
415 
416  overlays_ = utils::parenthetical_split(cfg["overlays"], ',');
417  if(overlays_.size() == 1 && overlays_.front() == "") {
418  overlays_.clear();
419  }
420  if (const config &variables = cfg.child("variables")) {
422  }
423 
424  if(vcfg) {
425  const vconfig& filter_recall = vcfg->child("filter_recall");
426  if(!filter_recall.null())
427  filter_recall_ = filter_recall.get_config();
428 
429  const vconfig::child_list& events = vcfg->get_children("event");
430  for(const vconfig& e : events) {
431  events_.add_child("event", e.get_config());
432  }
433  }
434  else
435  {
436  filter_recall_ = cfg.child_or_empty("filter_recall");
437 
438  for(const config& unit_event : cfg.child_range("event")) {
439  events_.add_child("event", unit_event);
440  }
441  }
442 
445  }
446 
447  random_traits_ = cfg["random_traits"].to_bool(true);
448  facing_ = map_location::parse_direction(cfg["facing"]);
450 
451  if (const config &mods = cfg.child("modifications")) {
452  modifications_ = mods;
453  }
454  generate_name_ = cfg["generate_name"].to_bool(true);
455  // Apply the unit type's data to this unit.
456  advance_to(*type_, use_traits);
457 
458  if (const config::attribute_value *v = cfg.get("race")) {
459  if (const unit_race *r = unit_types.find_race(*v)) {
460  race_ = r;
461  } else {
463  }
464  }
465  level_ = cfg["level"].to_int(level_);
466  if (const config::attribute_value *v = cfg.get("undead_variation")) {
467  undead_variation_ = v->str();
468  }
469  if(const config::attribute_value *v = cfg.get("max_attacks")) {
470  max_attacks_ = std::max(0, v->to_int(1));
471  }
472  attacks_left_ = std::max(0, cfg["attacks_left"].to_int(max_attacks_));
473 
474  if (const config::attribute_value *v = cfg.get("alpha")) {
475  alpha_ = lexical_cast_default<fixed_t>(*v);
476  }
477  if (const config::attribute_value *v = cfg.get("zoc")) {
478  emit_zoc_ = v->to_bool(level_ > 0);
479  }
480  if (const config::attribute_value *v = cfg.get("description")) {
481  description_ = *v;
482  }
483  if (const config::attribute_value *v = cfg.get("cost")) {
484  unit_value_ = *v;
485  }
486  if (const config::attribute_value *v = cfg.get("ellipse")) {
488  }
489  if (const config::attribute_value *v = cfg.get("halo")) {
490  set_image_halo(*v);
491  }
492  if (const config::attribute_value *v = cfg.get("usage")) {
493  set_usage(*v);
494  }
495 
496  if (const config::attribute_value *v = cfg.get("profile")) {
497  std::string profile = (*v).str();
498  adjust_profile(profile);
499  profile_ = profile;
500  }
501  if (const config::attribute_value *v = cfg.get("small_profile")) {
502  small_profile_ = (*v).str();
503  }
504 
505  max_hit_points_ = std::max(1, cfg["max_hitpoints"].to_int(max_hit_points_));
506  max_movement_ = std::max(0, cfg["max_moves"].to_int(max_movement_));
507  max_experience_ = std::max(1, cfg["max_experience"].to_int(max_experience_));
508 
509  vision_ = cfg["vision"].to_int(vision_);
510 
511  std::vector<std::string> temp_advances = utils::split(cfg["advances_to"]);
512  if(temp_advances.size() == 1 && temp_advances.front() == "null") {
513  advances_to_.clear();
514  }else if(temp_advances.size() >= 1 && temp_advances.front() != "") {
515  advances_to_ = temp_advances;
516  }
517 
518  if (const config &ai = cfg.child("ai"))
519  {
520  formula_man_->read(ai);
521  }
522 
523  //don't use the unit_type's attacks if this config has its own defined
524  config::const_child_itors cfg_range = cfg.child_range("attack");
525  if(cfg_range.first != cfg_range.second) {
526  attacks_.clear();
527  do {
528  attacks_.push_back(attack_type(*cfg_range.first));
529  } while(++cfg_range.first != cfg_range.second);
530  }
531 
532  //If cfg specifies [advancement]s, replace this [advancement]s with them.
533  if(cfg.has_child("advancement"))
534  {
535  this->advancements_.clear();
536  boost::copy( cfg.child_range("advancement"), boost::make_function_output_iterator(ptr_vector_pushback(advancements_)));
537  }
538 
539  //don't use the unit_type's abilities if this config has its own defined
540  //Why do we allow multiple [abilities] tags?
541  cfg_range = cfg.child_range("abilities");
542  if(cfg_range.first != cfg_range.second) {
543  this->abilities_.clear();
544  for(const config& abilities : cfg_range)
545  {
546  this->abilities_.append(abilities);
547  }
548  }
549 
550  // Adjust the unit's defense, movement, vision, jamming, resistances, and
551  // flying status if this config has its own defined.
552  movement_type_.merge(cfg);
553 
554  if (const config &status_flags = cfg.child("status"))
555  {
556  for(const config::attribute &st : status_flags.attribute_range()) {
557  if (st.second.to_bool()) {
558  set_state(st.first, true);
559  }
560  }
561  }
562  if(cfg["ai_special"] == "guardian") {
563  set_state(STATE_GUARDIAN, true);
564  }
565 
566  if (const config::attribute_value *v = cfg.get("hitpoints")) {
567  hit_points_ = *v;
568  } else {
570  }
571 
572  goto_.x = cfg["goto_x"].to_int() - 1;
573  goto_.y = cfg["goto_y"].to_int() - 1;
574 
575  if (const config::attribute_value *v = cfg.get("moves")) {
576  movement_ = *v;
577  if(movement_ < 0) {
578  attacks_left_ = 0;
579  movement_ = 0;
580  }
581  } else {
583  }
584  experience_ = cfg["experience"];
585  resting_ = cfg["resting"].to_bool();
586  unrenamable_ = cfg["unrenamable"].to_bool();
587 
588  /* We need to check to make sure that the cfg is not blank and if it
589  isn't pull that value otherwise it goes with the default of -1. */
590  if(!cfg["recall_cost"].blank()) {
591  recall_cost_ = cfg["recall_cost"].to_int(recall_cost_);
592  }
593 
594  alignment_.parse(cfg["alignment"].str());
595 
596  generate_name();
597 
598  parse_upkeep(cfg["upkeep"]);
599 
600  set_recruits(utils::split(cfg["extra_recruit"]));
601 
603  warn_unknown_attribute(cfg.attribute_range());
604  //debug unit animations for units as they appear in game
605  /*for(std::vector<unit_animation>::const_iterator i = anim_comp_->animations_.begin(); i != anim_comp_->animations_.end(); ++i) {
606  std::cout << (*i).debug();
607  }*/
608 }
609 
611 {
612  for(std::vector<const unit *>::const_iterator itor = units_with_cache.begin();
613  itor != units_with_cache.end(); ++itor) {
614  (*itor)->clear_visibility_cache();
615  }
616 
617  units_with_cache.clear();
618 }
619 
620 unit::unit(const unit_type &u_type, int side, bool real_unit, unit_race::GENDER gender)
621  : ref_count_(0)
622  , loc_()
623  , advances_to_()
624  , type_(&u_type)
625  , type_name_()
626  , race_(&unit_race::null_race)
627  , id_()
628  , name_()
629  , underlying_id_(real_unit? n_unit::unit_id(0) : resources::gameboard->unit_id_manager().next_fake_id())
630  , undead_variation_()
631  , variation_(type_->default_variation())
632  , hit_points_(0)
633  , max_hit_points_(0)
634  , experience_(0)
635  , max_experience_(0)
636  , level_(0)
637  , recall_cost_(-1)
638  , canrecruit_(false)
639  , recruit_list_()
640  , alignment_()
641  , flag_rgb_()
642  , image_mods_()
643  , unrenamable_(false)
644  , side_(side)
645  , gender_(gender != unit_race::NUM_GENDERS ?
646  gender : generate_gender(u_type, real_unit))
647  , alpha_()
648  , formula_man_(new unit_formula_manager())
649  , movement_(0)
650  , max_movement_(0)
651  , vision_(-1)
652  , jamming_(0)
653  , movement_type_()
654  , hold_position_(false)
655  , end_turn_(false)
656  , resting_(false)
657  , attacks_left_(0)
658  , max_attacks_(0)
659  , states_()
660  , known_boolean_states_( get_known_boolean_state_names().size(),false)
661  , variables_()
662  , events_()
663  , filter_recall_()
664  , emit_zoc_(0)
665  , overlays_()
666  , role_()
667  , attacks_()
668  , facing_(static_cast<map_location::DIRECTION>(rand()%map_location::NDIRECTIONS))
669  , trait_names_()
670  , trait_descriptions_()
671  , unit_value_()
672  , goto_()
673  , interrupted_move_()
674  , is_fearless_(false)
675  , is_healthy_(false)
676  , modification_descriptions_()
677  , anim_comp_(new unit_animation_component(*this))
678  , getsHit_(0)
679  , hidden_(false)
680  , modifications_()
681  , abilities_()
682  , advancements_()
683  , description_()
684  , usage_()
685  , halo_()
686  , ellipse_()
687  , random_traits_(true)
688  , generate_name_(true)
689  , upkeep_()
690  , invisibility_cache_()
691 {
692  upkeep_ = upkeep_full();
693 
694  // Apply the unit type's data to this unit.
695  advance_to(u_type, real_unit);
696 
697  if(real_unit) {
698  generate_name();
699  }
700  set_underlying_id(resources::gameboard->unit_id_manager());
701 
702  // Set these after traits and modifications have set the maximums.
706 }
707 
709 {
710  try {
711  anim_comp_->clear_haloes();
712 
713  // Remove us from the status cache
715  std::find(units_with_cache.begin(), units_with_cache.end(), this);
716 
717  if(itor != units_with_cache.end()) {
718  units_with_cache.erase(itor);
719  }
720  } catch (std::exception & e) {
721  ERR_UT << "Caught exception when destroying unit: " << e.what() << std::endl;
722  } catch (...) {}
723 }
724 
725 /**
726  * Swap, for copy and swap idiom
727  */
728 void unit::swap(unit & o)
729 {
730  using std::swap;
731 
732  // Don't swap reference count, or it will be incorrect...
733  swap(loc_, o.loc_);
735  swap(type_, o.type_);
737  swap(race_, o.race_);
738  swap(id_, o.id_);
739  swap(name_, o.name_);
747  swap(level_, o.level_);
755  swap(side_, o.side_);
756  swap(gender_, o.gender_);
757  swap(alpha_, o.alpha_);
761  swap(vision_, o.vision_);
762  swap(jamming_, o.jamming_);
766  swap(resting_, o.resting_);
769  swap(states_, o.states_);
772  swap(events_, o.events_);
776  swap(role_, o.role_);
777  swap(attacks_, o.attacks_);
778  swap(facing_, o.facing_);
782  swap(goto_, o.goto_);
788  swap(getsHit_, o.getsHit_);
789  swap(hidden_, o.hidden_);
792 }
793 
794 /**
795  * Assignment operator.
796  */
798 {
799  swap(other);
800  return *this;
801 }
802 
803 
805 {
806  if (!name_.empty() || !generate_name_) {
807  return;
808  }
810  generate_name_ = false;
811 }
812 
813 
814 /**
815  * Apply mandatory traits (e.g. undead, mechanical) to a unit and then
816  * fill out with available (leaders have a restricted set of available traits)
817  * traits until no more are available or the unit has its maximum number
818  * of traits.
819  * This routine does not apply the effects of added traits to a unit.
820  * That must be done by the caller.
821  * Note that random numbers used in config files don't work in multiplayer,
822  * so that leaders should be barred from all random traits until that
823  * is fixed. Later the restrictions will be based on play balance.
824  * @a musthaveonly is true when you don't want to generate random traits or
825  * you don't want to give any optional traits to a unit.
826  */
827 void unit::generate_traits(bool musthaveonly)
828 {
829  LOG_UT << "Generating a trait for unit type " << type().log_id() << " with musthaveonly " << musthaveonly << "\n";
830  const unit_type &u_type = type();
831 
832  // Calculate the unit's traits
833  config::const_child_itors current_traits = modifications_.child_range("trait");
834  std::vector<config> candidate_traits;
835 
836  for (const config &t : u_type.possible_traits())
837  {
838  // Skip the trait if the unit already has it.
839  const std::string &tid = t["id"];
840  bool already = false;
841  for (const config &mod : current_traits)
842  {
843  if (mod["id"] == tid) {
844  already = true;
845  break;
846  }
847  }
848  if (already) continue;
849 
850  // Add the trait if it is mandatory.
851  const std::string &avl = t["availability"];
852  if (avl == "musthave")
853  {
854  modifications_.add_child("trait", t);
855  current_traits = modifications_.child_range("trait");
856  continue;
857  }
858 
859  // The trait is still available, mark it as a candidate for randomizing.
860  // For leaders, only traits with availability "any" are considered.
861  if (!musthaveonly && (!can_recruit() || avl == "any"))
862  candidate_traits.push_back(t);
863  }
864 
865  if (musthaveonly) return;
866 
867  // Now randomly fill out to the number of traits required or until
868  // there aren't any more traits.
869  int nb_traits = std::distance(current_traits.first, current_traits.second);
870  int max_traits = u_type.num_traits();
871  for (; nb_traits < max_traits && !candidate_traits.empty(); ++nb_traits)
872  {
873  int num = random_new::generator->get_random_int(0,candidate_traits.size()-1);
874  modifications_.add_child("trait", candidate_traits[num]);
875  candidate_traits.erase(candidate_traits.begin() + num);
876  }
877 
878  // Once random traits are added, don't do it again.
879  // Such as when restoring a saved character.
880  random_traits_ = false;
881 }
882 
883 std::vector<std::string> unit::get_traits_list() const
884 {
885  std::vector<std::string> res;
886 
887  for (const config &mod : modifications_.child_range("trait"))
888  {
889  std::string const &id = mod["id"];
890  // Make sure to return empty id trait strings as otherwise
891  // names will not match in length (Bug #21967)
892  res.push_back(id);
893  }
894  return res;
895 }
896 
897 
898 /**
899  * Advances this unit to the specified type.
900  * Experience is left unchanged.
901  * Current hit point total is left unchanged unless it would violate max HP.
902  * Assumes gender_ and variation_ are set to their correct values.
903  */
904 void unit::advance_to(const unit_type &u_type,
905  bool use_traits)
906 {
907  // For reference, the type before this advancement.
908  const unit_type & old_type = type();
909  // Adjust the new type for gender and variation.
910  const unit_type & new_type = u_type.get_gender_unit_type(gender_).get_variation(variation_);
911 
912  // Reset the scalar values first
913  trait_names_.clear();
914  trait_descriptions_.clear(),
915  is_fearless_ = false;
916  is_healthy_ = false;
917 
918  // Clear modification-related caches
920 
921  // build unit type ready to create units. Not sure if needed.
922  new_type.get_cfg_for_units();
923 
924  if(!new_type.usage().empty()) {
925  set_usage(new_type.usage());
926  }
927  set_image_halo(new_type.halo());
928  set_image_ellipse(new_type.ellipse());
929  generate_name_ &= new_type.generate_name();
930  abilities_ = new_type.abilities_cfg();
931  advancements_.clear();
932  for(const config& advancement : new_type.advancements()) {
933  advancements_.push_back(new config(advancement));
934  }
935  // If unit has specific profile, remember it and keep it after advancing
936  if(small_profile_.empty() || small_profile_ == old_type.small_profile()) {
937  small_profile_ = new_type.small_profile();
938  }
939  if(profile_.empty() || profile_ == old_type.big_profile()) {
940  profile_ = new_type.big_profile();
941  }
942  // NOTE: There should be no need to access old_cfg (or new_cfg) after this
943  // line. Particularly since the swap might have affected old_cfg.
944 
945  advances_to_ = new_type.advances_to();
946 
947  race_ = new_type.race();
948  type_ = &new_type;
949  type_name_ = new_type.type_name();
950  description_ = new_type.unit_description();
951  undead_variation_ = new_type.undead_variation();
952  max_experience_ = new_type.experience_needed(false);
953  level_ = new_type.level();
954  recall_cost_ = new_type.recall_cost();
955  /* Need to add a check to see if the unit's old cost is equal
956  to the unit's old unit_type cost first. If it is change the cost
957  otherwise keep the old cost. */
958  if(old_type.recall_cost() == recall_cost_) {
959  recall_cost_ = new_type.recall_cost();
960  }
961  alignment_ = new_type.alignment();
962  alpha_ = new_type.alpha();
963  max_hit_points_ = new_type.hitpoints();
964  hp_bar_scaling_ = new_type.hp_bar_scaling();
965  xp_bar_scaling_ = new_type.xp_bar_scaling();
966  max_movement_ = new_type.movement();
967  vision_ = new_type.vision(true);
968  jamming_ = new_type.jamming();
969  movement_type_ = new_type.movement_type();
970  emit_zoc_ = new_type.has_zoc();
971  attacks_ = new_type.attacks();
972  unit_value_ = new_type.cost();
973 
974  max_attacks_ = new_type.max_attacks();
975 
976  flag_rgb_ = new_type.flag_rgb();
977 
978  anim_comp_->reset_after_advance(&new_type);
979 
980  if (random_traits_) {
981  generate_traits(!use_traits);
982  } else {
983  // This will add any "musthave" traits to the new unit that it doesn't already have.
984  // This covers the Dark Sorcerer advancing to Lich and gaining the "undead" trait,
985  // but random and/or optional traits are not added,
986  // and neither are inappropriate traits removed.
987  generate_traits(true);
988  }
989 
990  // Apply modifications etc, refresh the unit.
991  // This needs to be after type and gender are fixed,
992  // since there can be filters on the modifications
993  // that may result in different effects after the advancement.
995 
996  // Now that modifications are done modifying traits, check if poison should
997  // be cleared.
998  if ( get_state("unpoisonable") )
999  set_state(STATE_POISONED, false);
1000 
1001  // Now that modifications are done modifying the maximum hit points,
1002  // enforce this maximum.
1003  if ( hit_points_ > max_hit_points_ )
1005 
1006  // In case the unit carries EventWML, apply it now
1007  if (resources::game_events) {
1008  resources::game_events->add_events(new_type.events(), new_type.id());
1009  }
1010 }
1011 
1013 {
1014  if (!profile_.empty() && profile_ != "unit_image") {
1015  return profile_;
1016  }
1017  return absolute_image();
1018 }
1019 
1021 {
1022  if (!small_profile_.empty() && small_profile_ != "unit_image") {
1023  return small_profile_;
1024  }
1025  return absolute_image();
1026 }
1027 
1028 static SDL_Color hp_color_(int hitpoints, int max_hitpoints)
1029 {
1030  double unit_energy = 0.0;
1031  SDL_Color energy_color = {0,0,0,0};
1032 
1033  if(max_hitpoints > 0) {
1034  unit_energy = double(hitpoints)/double(max_hitpoints);
1035  }
1036 
1037  if(1.0 == unit_energy){
1038  energy_color.r = 33;
1039  energy_color.g = 225;
1040  energy_color.b = 0;
1041  } else if(unit_energy > 1.0) {
1042  energy_color.r = 100;
1043  energy_color.g = 255;
1044  energy_color.b = 100;
1045  } else if(unit_energy >= 0.75) {
1046  energy_color.r = 170;
1047  energy_color.g = 255;
1048  energy_color.b = 0;
1049  } else if(unit_energy >= 0.5) {
1050  energy_color.r = 255;
1051  energy_color.g = 175;
1052  energy_color.b = 0;
1053  } else if(unit_energy >= 0.25) {
1054  energy_color.r = 255;
1055  energy_color.g = 155;
1056  energy_color.b = 0;
1057  } else {
1058  energy_color.r = 255;
1059  energy_color.g = 0;
1060  energy_color.b = 0;
1061  }
1062  return energy_color;
1063 }
1064 
1065 SDL_Color unit::hp_color() const
1066 {
1067  return hp_color_(hitpoints(), max_hitpoints());
1068 }
1069 
1070 SDL_Color unit::hp_color(int new_hitpoints) const
1071 {
1072  return hp_color_(new_hitpoints, hitpoints());
1073 }
1074 
1075 SDL_Color unit::xp_color() const
1076 {
1077  const SDL_Color near_advance_color = {255,255,255,0};
1078  const SDL_Color mid_advance_color = {150,255,255,0};
1079  const SDL_Color far_advance_color = {0,205,205,0};
1080  const SDL_Color normal_color = {0,160,225,0};
1081  const SDL_Color near_amla_color = {225,0,255,0};
1082  const SDL_Color mid_amla_color = {169,30,255,0};
1083  const SDL_Color far_amla_color = {139,0,237,0};
1084  const SDL_Color amla_color = {170,0,255,0};
1085  const bool near_advance = max_experience() - experience() <= game_config::kill_experience;
1086  const bool mid_advance = max_experience() - experience() <= game_config::kill_experience*2;
1087  const bool far_advance = max_experience() - experience() <= game_config::kill_experience*3;
1088 
1089  SDL_Color color=normal_color;
1090  if(advances_to().size()){
1091  if(near_advance){
1092  color=near_advance_color;
1093  } else if(mid_advance){
1094  color=mid_advance_color;
1095  } else if(far_advance){
1096  color=far_advance_color;
1097  }
1098  } else if (get_modification_advances().size()){
1099  if(near_advance){
1100  color=near_amla_color;
1101  } else if(mid_advance){
1102  color=mid_amla_color;
1103  } else if(far_advance){
1104  color=far_amla_color;
1105  } else {
1106  color=amla_color;
1107  }
1108  }
1109  return(color);
1110 }
1111 
1112 void unit::set_recruits(const std::vector<std::string>& recruits)
1113 {
1114  unit_types.check_types(recruits);
1116  //TODO crab
1117  //info_.minimum_recruit_price = 0;
1118  //ai::manager::raise_recruit_list_changed();
1119 }
1120 
1121 const std::vector<std::string> unit::advances_to_translated() const
1122 {
1123  std::vector<std::string> result;
1124  for (std::string adv_type_id : advances_to_)
1125  {
1126  const unit_type *adv_type = unit_types.find(adv_type_id);
1127  if ( adv_type )
1128  result.push_back(adv_type->type_name());
1129  else
1130  WRN_UT << "unknown unit in advances_to list of type "
1131  << type().log_id() << ": " << adv_type_id << "\n";
1132  }
1133  return result;
1134 }
1135 
1136 void unit::set_advances_to(const std::vector<std::string>& advances_to)
1137 {
1138  unit_types.check_types(advances_to);
1140 }
1141 
1142 /**
1143  * Set the unit's remaining movement to @a moves.
1144  * If @a unit_action is set to true, then additionally the "end turn" and
1145  * "hold position" flags will be cleared (as they should be if a unit acts,
1146  * as opposed to the movement being set by the engine for other reasons).
1147  */
1148 void unit::set_movement(int moves, bool unit_action)
1149 {
1150  // If this was because the unit acted, clear its "not acting" flags.
1151  if ( unit_action )
1152  end_turn_ = hold_position_ = false;
1153 
1154  movement_ = std::max<int>(0, moves);
1155 }
1156 
1157 
1158 /**
1159  * Determines if @a mod_dur "matches" @a goal_dur.
1160  * If goal_dur is not empty, they match if they are equal.
1161  * If goal_dur is empty, they match if mod_dur is neither empty nor "forever".
1162  * Helper function for expire_modifications().
1163  */
1164 inline bool mod_duration_match(const std::string & mod_dur,
1165  const std::string & goal_dur)
1166 {
1167  if ( goal_dur.empty() )
1168  // Default is all temporary modifications.
1169  return !mod_dur.empty() && mod_dur != "forever";
1170  else
1171  return mod_dur == goal_dur;
1172 }
1173 
1174 /**
1175  * Clears those modifications whose duration has expired.
1176  * If @a duration is empty, then all temporary modifications (those not
1177  * lasting forever) have expired. Otherwise, modifications whose duration
1178  * equals @a duration have expired.
1179  */
1181 {
1182  // If any modifications expire, then we will need to rebuild the unit.
1183  const unit_type * rebuild_from = nullptr;
1184 
1185  // Loop through all types of modifications.
1186  for(unsigned int i = 0; i != NumModificationTypes; ++i) {
1187  const std::string& mod_name = ModificationTypes[i];
1188  // Loop through all modifications of this type.
1189  // Looping in reverse since we may delete the current modification.
1190  for (int j = modifications_.child_count(mod_name)-1; j >= 0; --j)
1191  {
1192  const config &mod = modifications_.child(mod_name, j);
1193 
1194  if ( mod_duration_match(mod["duration"], duration) ) {
1195  // If removing this mod means reverting the unit's type:
1196  if ( const config::attribute_value *v = mod.get("prev_type") ) {
1197  rebuild_from = &get_unit_type(v->str());
1198  }
1199  // Else, if we have not already specified a type to build from:
1200  else if ( rebuild_from == nullptr )
1201  rebuild_from = &type();
1202 
1203  modifications_.remove_child(mod_name, j);
1204  }
1205  }
1206  }
1207 
1208  if ( rebuild_from != nullptr ) {
1209  anim_comp_->clear_haloes();
1210  advance_to(*rebuild_from);
1211  }
1212 }
1213 
1214 
1216 {
1217  expire_modifications("turn");
1218 
1222  set_state(STATE_UNCOVERED, false);
1223 }
1224 
1226 {
1227  expire_modifications("turn end");
1228 
1229  set_state(STATE_SLOWED,false);
1231  resting_ = false;
1232  }
1233  set_state(STATE_NOT_MOVED,false);
1234  // Clear interrupted move
1236 }
1237 
1239 {
1240  // Set the goto-command to be going to no-where
1241  goto_ = map_location();
1242 
1243  // Expire all temporary modifications.
1245 
1246  heal_all();
1247  set_state(STATE_SLOWED, false);
1248  set_state(STATE_POISONED, false);
1249  set_state(STATE_PETRIFIED, false);
1250  set_state(STATE_GUARDIAN, false);
1251 }
1252 
1254 {
1255  int max_hp = max_hitpoints();
1256  if (hit_points_ < max_hp) {
1257  hit_points_ += amount;
1258  if (hit_points_ > max_hp) {
1259  hit_points_ = max_hp;
1260  }
1261  }
1262  if(hit_points_<1) {
1263  hit_points_ = 1;
1264  }
1265 }
1266 
1267 const std::map<std::string,std::string> unit::get_states() const
1268 {
1269  std::map<std::string, std::string> all_states;
1270  for (std::string const &s : states_) {
1271  all_states[s] = "yes";
1272  }
1273  for (std::map<std::string, state_t>::const_iterator i = known_boolean_state_names_.begin(),
1274  i_end = known_boolean_state_names_.end(); i != i_end; ++i)
1275  {
1276  if (get_state(i->second)) {
1277  all_states.insert(make_pair(i->first, "yes"));
1278  }
1279 
1280  }
1281  // Backwards compatibility for not_living. Don't remove before 1.12
1282  if (all_states.find("undrainable") != all_states.end() &&
1283  all_states.find("unpoisonable") != all_states.end() &&
1284  all_states.find("unplagueable") != all_states.end())
1285  all_states["not_living"] = "yes";
1286  return all_states;
1287 }
1288 
1289 bool unit::get_state(const std::string &state) const
1290 {
1291  state_t known_boolean_state_id = get_known_boolean_state_id(state);
1292  if (known_boolean_state_id!=STATE_UNKNOWN){
1293  return get_state(known_boolean_state_id);
1294  }
1295  // Backwards compatibility for not_living. Don't remove before 1.12
1296  if (state == "not_living") {
1297  return get_state("undrainable") &&
1298  get_state("unpoisonable") &&
1299  get_state("unplagueable");
1300  }
1301  return states_.find(state) != states_.end();
1302 }
1303 
1304 void unit::set_state(state_t state, bool value)
1305 {
1306  known_boolean_states_[state] = value;
1307 }
1308 
1309 bool unit::get_state(state_t state) const
1310 {
1311  return known_boolean_states_[state];
1312 }
1313 
1315  std::map<std::string, state_t>::const_iterator i = known_boolean_state_names_.find(state);
1316  if (i != known_boolean_state_names_.end()) {
1317  return i->second;
1318  }
1319  return STATE_UNKNOWN;
1320 }
1321 
1322 std::map<std::string, unit::state_t> unit::known_boolean_state_names_ = get_known_boolean_state_names();
1323 
1324 std::map<std::string, unit::state_t> unit::get_known_boolean_state_names()
1325 {
1326  std::map<std::string, state_t> known_boolean_state_names_map;
1327  known_boolean_state_names_map.insert(std::make_pair("slowed",STATE_SLOWED));
1328  known_boolean_state_names_map.insert(std::make_pair("poisoned",STATE_POISONED));
1329  known_boolean_state_names_map.insert(std::make_pair("petrified",STATE_PETRIFIED));
1330  known_boolean_state_names_map.insert(std::make_pair("uncovered", STATE_UNCOVERED));
1331  known_boolean_state_names_map.insert(std::make_pair("not_moved",STATE_NOT_MOVED));
1332  known_boolean_state_names_map.insert(std::make_pair("unhealable",STATE_UNHEALABLE));
1333  known_boolean_state_names_map.insert(std::make_pair("guardian",STATE_GUARDIAN));
1334  return known_boolean_state_names_map;
1335 }
1336 
1337 void unit::set_state(const std::string &state, bool value)
1338 {
1339  state_t known_boolean_state_id = get_known_boolean_state_id(state);
1340  if (known_boolean_state_id != STATE_UNKNOWN) {
1341  set_state(known_boolean_state_id, value);
1342  return;
1343  }
1344  // Backwards compatibility for not_living. Don't remove before 1.12
1345  if (state == "not_living") {
1346  set_state("undrainable", value);
1347  set_state("unpoisonable", value);
1348  set_state("unplagueable", value);
1349  }
1350  if (value)
1351  states_.insert(state);
1352  else
1353  states_.erase(state);
1354 }
1355 
1356 
1357 bool unit::has_ability_by_id(const std::string& ability) const
1358 {
1359  for (const config::any_child &ab : this->abilities_.all_children_range()) {
1360  if (ab.cfg["id"] == ability) {
1361  return true;
1362  }
1363  }
1364  return false;
1365 }
1366 
1368 {
1370  while (i != this->abilities_.ordered_end()) {
1371  if (i->cfg["id"] == ability) {
1372  i = this->abilities_.erase(i);
1373  } else {
1374  ++i;
1375  }
1376  }
1377 }
1378 
1379 void unit::write(config& cfg) const
1380 {
1381  movement_type_.write(cfg);
1382  cfg["small_profile"] = small_profile_;
1383  cfg["profile"] = profile_;
1384  if ( description_ != type().unit_description() ) {
1385  cfg["description"] = description_;
1386  }
1387  if(halo_.get()) {
1388  cfg["halo"] = *halo_;
1389  }
1390  if(ellipse_.get()) {
1391  cfg["ellipse"] = *ellipse_;
1392  }
1393  if(usage_.get()) {
1394  cfg["usage"] = *usage_;
1395  }
1396  write_upkeep(cfg["upkeep"]);
1397  cfg["hitpoints"] = hit_points_;
1398  cfg["max_hitpoints"] = max_hit_points_;
1399 
1400  cfg["image_icon"] = type().icon();
1401  cfg["image"] = type().image();
1402  cfg["random_traits"] = random_traits_;
1403  cfg["generate_name"] = generate_name_;
1404  cfg["experience"] = experience_;
1405  cfg["max_experience"] = max_experience_;
1406  cfg["recall_cost"] = recall_cost_;
1407 
1408  cfg["side"] = side_;
1409 
1410  cfg["type"] = type_id();
1411  if ( type_id() != type().base_id() )
1412  cfg["parent_type"] = type().base_id();
1413 
1414  //support for unit formulas in [ai] and unit-specific variables in [ai] [vars]
1415 
1416  formula_man_->write(cfg);
1417 
1418 
1419  cfg["gender"] = gender_string(gender_);
1420  cfg["variation"] = variation_;
1421  cfg["role"] = role_;
1422 
1423  config status_flags;
1424  std::map<std::string,std::string> all_states = get_states();
1425  for(std::map<std::string,std::string>::const_iterator st = all_states.begin(); st != all_states.end(); ++st) {
1426  status_flags[st->first] = st->second;
1427  }
1428 
1429  cfg.clear_children("variables");
1430  cfg.add_child("variables",variables_);
1431  cfg.clear_children("events");
1432  cfg.append(events_);
1433  cfg.clear_children("filter_recall");
1434  cfg.add_child("filter_recall", filter_recall_);
1435  cfg.clear_children("status");
1436  cfg.add_child("status",status_flags);
1437 
1438  cfg["overlays"] = utils::join(overlays_);
1439 
1440  cfg["name"] = name_;
1441  cfg["id"] = id_;
1442  cfg["underlying_id"] = underlying_id_.value;
1443  if(can_recruit())
1444  cfg["canrecruit"] = true;
1445 
1446  cfg["extra_recruit"] = utils::join(recruit_list_);
1447 
1448  cfg["facing"] = map_location::write_direction(facing_);
1449 
1450  cfg["goto_x"] = goto_.x + 1;
1451  cfg["goto_y"] = goto_.y + 1;
1452 
1453  cfg["moves"] = movement_;
1454  cfg["max_moves"] = max_movement_;
1455  cfg["vision"] = vision_;
1456  cfg["jamming"] = jamming_;
1457 
1458  cfg["resting"] = resting_;
1459 
1460  cfg["advances_to"] = utils::join(advances_to_);
1461 
1462  cfg["race"] = race_->id();
1463  cfg["language_name"] = type_name_;
1464  cfg["undead_variation"] = undead_variation_;
1465  cfg["level"] = level_;
1466  cfg["alignment"] = alignment_.to_string();
1467  cfg["flag_rgb"] = flag_rgb_;
1468  cfg["unrenamable"] = unrenamable_;
1469  cfg["alpha"] = std::to_string(alpha_);
1470 
1471  cfg["attacks_left"] = attacks_left_;
1472  cfg["max_attacks"] = max_attacks_;
1473  cfg["zoc"] = emit_zoc_;
1474  cfg.clear_children("attack");
1475  for(std::vector<attack_type>::const_iterator i = attacks_.begin(); i != attacks_.end(); ++i) {
1476  i->write(cfg.add_child("attack"));
1477  }
1478  cfg["cost"] = unit_value_;
1479  cfg.clear_children("modifications");
1480  cfg.add_child("modifications", modifications_);
1481  cfg.clear_children("abilities");
1482  cfg.add_child("abilities", abilities_);
1483  cfg.clear_children("advancement");
1484  for(const config& advancement : this->advancements_)
1485  {
1486  cfg.add_child("advancement", advancement);
1487  }
1488 
1489 }
1490 
1492  if(dir != map_location::NDIRECTIONS) {
1493  facing_ = dir;
1494  }
1495  // Else look at yourself (not available so continue to face the same direction)
1496 }
1497 
1498 int unit::upkeep() const
1499 {
1500  // Leaders do not incur upkeep.
1501  if(can_recruit()) {
1502  return 0;
1503  }
1504  else if(boost::get<upkeep_full>(&upkeep_) != nullptr) {
1505  return level();
1506  }
1507  else if(boost::get<upkeep_loyal>(&upkeep_) != nullptr) {
1508  return 0;
1509  }
1510  else {
1511  return boost::get<int>(upkeep_);
1512  }
1513 }
1514 
1515 bool unit::loyal() const
1516 {
1517  return boost::get<upkeep_loyal>(&upkeep_) != nullptr;
1518 }
1519 
1521 {
1522  int def = movement_type_.defense_modifier(terrain);
1523 #if 0
1524  // A [defense] ability is too costly and doesn't take into account target locations.
1525  // Left as a comment in case someone ever wonders why it isn't a good idea.
1526  unit_ability_list defense_abilities = get_abilities("defense");
1527  if (!defense_abilities.empty()) {
1528  unit_abilities::effect defense_effect(defense_abilities, def, false);
1529  def = defense_effect.get_composite_value();
1530  }
1531 #endif
1532  return def;
1533 }
1534 
1535 bool unit::resistance_filter_matches(const config& cfg, bool attacker, const std::string& damage_name, int res) const
1536 {
1537  if(!(cfg["active_on"].empty() || (attacker && cfg["active_on"]=="offense") || (!attacker && cfg["active_on"]=="defense"))) {
1538  return false;
1539  }
1540  const std::string& apply_to = cfg["apply_to"];
1541  if(!apply_to.empty()) {
1542  if(damage_name != apply_to) {
1543  if ( apply_to.find(',') != std::string::npos &&
1544  apply_to.find(damage_name) != std::string::npos ) {
1545  const std::vector<std::string>& vals = utils::split(apply_to);
1546  if(std::find(vals.begin(),vals.end(),damage_name) == vals.end()) {
1547  return false;
1548  }
1549  } else {
1550  return false;
1551  }
1552  }
1553  }
1554  if (!unit_abilities::filter_base_matches(cfg, res)) return false;
1555  return true;
1556 }
1557 
1558 
1559 int unit::resistance_against(const std::string& damage_name,bool attacker,const map_location& loc) const
1560 {
1561  int res = movement_type_.resistance_against(damage_name);
1562 
1563  unit_ability_list resistance_abilities = get_abilities("resistance",loc);
1564  for (unit_ability_list::iterator i = resistance_abilities.begin(); i != resistance_abilities.end();) {
1565  if(!resistance_filter_matches(*i->first, attacker, damage_name, 100-res)) {
1566  i = resistance_abilities.erase(i);
1567  } else {
1568  ++i;
1569  }
1570  }
1571  if(!resistance_abilities.empty()) {
1572  unit_abilities::effect resist_effect(resistance_abilities, 100-res, false);
1573 
1574  res = 100 - std::min<int>(resist_effect.get_composite_value(),
1575  resistance_abilities.highest("max_value").first);
1576  }
1577  return res;
1578 }
1579 
1580 std::map<std::string,std::string> unit::advancement_icons() const
1581 {
1582  std::map<std::string,std::string> temp;
1583  if (!can_advance())
1584  return temp;
1585 
1586  if (!advances_to_.empty())
1587  {
1588  std::ostringstream tooltip;
1590  for (const std::string &s : advances_to())
1591  {
1592  if (!s.empty())
1593  tooltip << s << '\n';
1594  }
1595  temp[image] = tooltip.str();
1596  }
1597 
1598  for (const config &adv : get_modification_advances())
1599  {
1600  const std::string &image = adv["image"];
1601  if (image.empty()) continue;
1602  std::ostringstream tooltip;
1603  tooltip << temp[image];
1604  const std::string &tt = adv["description"];
1605  if (!tt.empty())
1606  tooltip << tt << '\n';
1607  temp[image] = tooltip.str();
1608  }
1609  return(temp);
1610 }
1611 std::vector<std::pair<std::string,std::string> > unit::amla_icons() const
1612 {
1613  std::vector<std::pair<std::string,std::string> > temp;
1614  std::pair<std::string,std::string> icon; // <image,tooltip>
1615 
1616  for (const config &adv : get_modification_advances())
1617  {
1618  icon.first = adv["icon"].str();
1619  icon.second = adv["description"].str();
1620 
1621  for (unsigned j = 0, j_count = modification_count("advancement", adv["id"]);
1622  j < j_count; ++j)
1623  {
1624  temp.push_back(icon);
1625  }
1626  }
1627  return(temp);
1628 }
1629 
1630 std::vector<config> unit::get_modification_advances() const
1631 {
1632  std::vector<config> res;
1633  for (const config &adv : modification_advancements())
1634  {
1635  if (adv["strict_amla"].to_bool() && !advances_to_.empty())
1636  continue;
1637  if (modification_count("advancement", adv["id"]) >= unsigned(adv["max_times"].to_int(1)))
1638  continue;
1639 
1640  std::vector<std::string> temp_require = utils::split(adv["require_amla"]);
1641  std::vector<std::string> temp_exclude = utils::split(adv["exclude_amla"]);
1642  if (temp_require.empty() && temp_exclude.empty()) {
1643  res.push_back(adv);
1644  continue;
1645  }
1646 
1647  std::sort(temp_require.begin(), temp_require.end());
1648  std::sort(temp_exclude.begin(), temp_exclude.end());
1649  std::vector<std::string> uniq_require, uniq_exclude;
1650  std::unique_copy(temp_require.begin(), temp_require.end(), std::back_inserter(uniq_require));
1651  std::unique_copy(temp_exclude.begin(), temp_exclude.end(), std::back_inserter(uniq_exclude));
1652 
1653  bool exclusion_found = false;
1654  for (const std::string &s : uniq_exclude)
1655  {
1656  int max_num = std::count(temp_exclude.begin(), temp_exclude.end(), s);
1657  int mod_num = modification_count("advancement", s);
1658  if (mod_num >= max_num) {
1659  exclusion_found = true;
1660  break;
1661  }
1662  }
1663  if (exclusion_found) {
1664  continue;
1665  }
1666 
1667  bool requirements_done = true;
1668  for (const std::string &s : uniq_require)
1669  {
1670  int required_num = std::count(temp_require.begin(), temp_require.end(), s);
1671  int mod_num = modification_count("advancement", s);
1672  if (required_num > mod_num) {
1673  requirements_done = false;
1674  break;
1675  }
1676  }
1677  if (requirements_done) {
1678  res.push_back(adv);
1679  }
1680  }
1681 
1682  return res;
1683 }
1684 
1685 void unit::set_advancements(std::vector<config> advancements)
1686 {
1687  this->advancements_.clear();
1688  for (config& advancement : advancements)
1689  {
1690  this->advancements_.push_back(new config());
1691  this->advancements_.back().swap(advancement);
1692  }
1693 }
1694 
1695 size_t unit::modification_count(const std::string& mod_type, const std::string& id) const
1696 {
1697  size_t res = 0;
1698  for (const config &item : modifications_.child_range(mod_type)) {
1699  if (item["id"] == id) {
1700  ++res;
1701  }
1702  }
1703 
1704  // For backwards compatibility, if asked for "advancement", also count "advance"
1705  if (mod_type == "advancement") {
1706  res += modification_count("advance", id);
1707  }
1708 
1709  return res;
1710 }
1711 
1712 const std::set<std::string> unit::builtin_effects = {
1713  "alignment", "attack", "defense", "ellipse", "experience", "fearless",
1714  "halo", "healthy", "hitpoints", "image_mod", "jamming", "jamming_costs",
1715  "loyal", "max_attacks", "max_experience", "movement", "movement_costs",
1716  "new_ability", "new_advancement", "new_animation", "new_attack", "overlay", "profile",
1717  "recall_cost", "remove_ability", "remove_advancement", "remove_attacks", "resistance",
1718  "status", "type", "variation", "vision", "vision_costs", "zoc"};
1719 
1721 {
1722  if(apply_to == "attack") {
1723  std::string attack_names;
1724  bool first_attack = true;
1725 
1726  std::string desc;
1728  a != attacks_.end(); ++a) {
1729  bool affected = a->describe_modification(effect, &desc);
1730  if(affected && desc != "") {
1731  if(first_attack) {
1732  first_attack = false;
1733  } else {
1734  attack_names += t_string(N_(" and "), "wesnoth");
1735  }
1736 
1737  attack_names += t_string(a->name(), "wesnoth-units");
1738  }
1739  }
1740  if (!attack_names.empty()) {
1741  utils::string_map symbols;
1742  symbols["attack_list"] = attack_names;
1743  symbols["effect_description"] = desc;
1744  return vgettext("$attack_list|: $effect_description", symbols);
1745  }
1746  } else if(apply_to == "hitpoints") {
1747  const std::string &increase_total = effect["increase_total"];
1748 
1749  if(!increase_total.empty()) {
1750  return utils::print_modifier(increase_total) + " " +
1751  t_string(N_("HP"), "wesnoth");
1752  }
1753  } else if(apply_to == "movement") {
1754  const std::string &increase = effect["increase"];
1755 
1756  if(!increase.empty()) {
1757  int n = lexical_cast<int>(increase);
1758  return utils::print_modifier(increase) + " " +
1759  _n("move", "moves", n);
1760  }
1761  } else if(apply_to == "vision") {
1762  const std::string &increase = effect["increase"];
1763 
1764  if(!increase.empty()) {
1765  return utils::print_modifier(increase) + " " + t_string(N_("vision"), "wesnoth");
1766  }
1767  } else if(apply_to == "jamming") {
1768  const std::string &increase = effect["increase"];
1769 
1770  if(!increase.empty()) {
1771  return utils::print_modifier(increase) + " " + t_string(N_("jamming"), "wesnoth");
1772  }
1773  } else if(apply_to == "max_experience") {
1774  const std::string &increase = effect["increase"];
1775 
1776  if(!increase.empty()) {
1777  return utils::print_modifier(increase) + " " +
1778  t_string(N_("XP to advance"), "wesnoth");
1779  }
1780  } else if (apply_to == "max_attacks") {
1781  const std::string &increase = effect["increase"];
1782 
1783  std::string description = utils::print_modifier(increase) + " ";
1784  const char* const singular = N_("attack per turn");
1785  const char* const plural = N_("attacks per turn");
1786  if (increase[increase.size()-1] == '%' || abs(lexical_cast<int>(increase)) != 1) {
1787  description += t_string(plural, "wesnoth");
1788  } else {
1789  description += t_string(singular, "wesnoth");
1790  }
1791  return description;
1792  } else if (apply_to == "recall_cost") {
1793  const std::string &increase = effect["increase"];
1794  return utils::print_modifier(increase) + " " +
1795  t_string(N_("cost to recall"), "wesnoth");
1796  }
1797  return "";
1798 }
1799 
1800 void unit::apply_builtin_effect(std::string apply_to, const config& effect)
1801 {
1802  if(apply_to == "fearless")
1803  {
1804  is_fearless_ = effect["set"].to_bool(true);
1805  }
1806  else if(apply_to == "healthy")
1807  {
1808  is_healthy_ = effect["set"].to_bool(true);
1809  }
1810  else if(apply_to == "profile") {
1811  if (const config::attribute_value *v = effect.get("portrait")) {
1812  std::string portrait = (*v).str();
1813  adjust_profile(portrait);
1814  profile_ = portrait;
1815  }
1816  if (const config::attribute_value *v = effect.get("small_portrait")) {
1817  small_profile_ = (*v).str();
1818  }
1819  if (const config::attribute_value *v = effect.get("description")) {
1820  description_ = *v;
1821  }
1822  } else if(apply_to == "new_attack") {
1823  attacks_.push_back(attack_type(effect));
1824  } else if(apply_to == "remove_attacks") {
1826  while(a != attacks_.end()) {
1827  if(a->matches_filter(effect)) {
1828  a = attacks_.erase(a);
1829  continue;
1830  }
1831  ++a;
1832  }
1833  } else if(apply_to == "attack") {
1835  a != attacks_.end(); ++a) {
1836  a->apply_modification(effect);
1837  }
1838  } else if(apply_to == "hitpoints") {
1839  LOG_UT << "applying hitpoint mod..." << hit_points_ << "/" << max_hit_points_ << "\n";
1840  const std::string &increase_hp = effect["increase"];
1841  const std::string &increase_total = effect["increase_total"];
1842  const std::string &set_hp = effect["set"];
1843  const std::string &set_total = effect["set_total"];
1844 
1845  // If the hitpoints are allowed to end up greater than max hitpoints
1846  const bool violate_max = effect["violate_maximum"].to_bool();
1847 
1848  if(!set_hp.empty()) {
1849  if(set_hp[set_hp.size()-1] == '%') {
1850  hit_points_ = lexical_cast_default<int>(set_hp)*max_hit_points_/100;
1851  } else {
1852  hit_points_ = lexical_cast_default<int>(set_hp);
1853  }
1854  }
1855  if(!set_total.empty()) {
1856  if(set_total[set_total.size()-1] == '%') {
1857  max_hit_points_ = lexical_cast_default<int>(set_total)*max_hit_points_/100;
1858  } else {
1859  max_hit_points_ = lexical_cast_default<int>(set_total);
1860  }
1861  }
1862 
1863  if(!increase_total.empty()) {
1864  // A percentage on the end means increase by that many percent
1865  max_hit_points_ = utils::apply_modifier(max_hit_points_, increase_total);
1866  }
1867 
1868  if(max_hit_points_ < 1)
1869  max_hit_points_ = 1;
1870 
1871  if (effect["heal_full"].to_bool()) {
1872  heal_all();
1873  }
1874 
1875  if(!increase_hp.empty()) {
1877  }
1878 
1879  LOG_UT << "modded to " << hit_points_ << "/" << max_hit_points_ << "\n";
1880  if(hit_points_ > max_hit_points_ && !violate_max) {
1881  LOG_UT << "resetting hp to max\n";
1883  }
1884 
1885  if(hit_points_ < 1)
1886  hit_points_ = 1;
1887  } else if(apply_to == "movement") {
1888  const std::string &increase = effect["increase"];
1889 
1890  if(!increase.empty()) {
1892  }
1893 
1894  max_movement_ = effect["set"].to_int(max_movement_);
1895 
1896  if(movement_ > max_movement_)
1898  } else if(apply_to == "vision") {
1899  const std::string &increase = effect["increase"];
1900 
1901  if(!increase.empty()) {
1902  const int current_vision = vision_ < 0 ? max_movement_ : vision_;
1903  vision_ = utils::apply_modifier(current_vision, increase, 1);
1904  }
1905 
1906  vision_ = effect["set"].to_int(vision_);
1907  } else if(apply_to == "jamming") {
1908  const std::string &increase = effect["increase"];
1909 
1910  if(!increase.empty()) {
1911  jamming_ = utils::apply_modifier(jamming_, increase, 1);
1912  }
1913 
1914  jamming_ = effect["set"].to_int(jamming_);
1915  } else if(apply_to == "experience") {
1916  const std::string &increase = effect["increase"];
1917  const std::string &set = effect["set"];
1918 
1919  if(!set.empty()) {
1920  if(set[set.size()-1] == '%') {
1921  experience_ = lexical_cast_default<int>(set)*max_experience_/100;
1922  } else {
1923  experience_ = lexical_cast_default<int>(set);
1924  }
1925  }
1926 
1927  if(increase.empty() == false) {
1929  }
1930  } else if(apply_to == "max_experience") {
1931  const std::string &increase = effect["increase"];
1932  const std::string &set = effect["set"];
1933 
1934  if(set.empty() == false) {
1935  if(set[set.size()-1] == '%') {
1936  max_experience_ = lexical_cast_default<int>(set)*max_experience_/100;
1937  } else {
1938  max_experience_ = lexical_cast_default<int>(set);
1939  }
1940  }
1941 
1942  if(increase.empty() == false) {
1944  }
1945  } else if(apply_to == "loyal") {
1946  upkeep_ = upkeep_loyal();;
1947  } else if(apply_to == "status") {
1948  const std::string& add = effect["add"];
1949  const std::string& remove = effect["remove"];
1950 
1951  for (const std::string& to_add : utils::split(add))
1952  {
1953  set_state(to_add, true);
1954  }
1955 
1956  for (const std::string& to_remove : utils::split(remove))
1957  {
1958  set_state(to_remove, false);
1959  }
1960  // Note: It would not be hard to define a new "applies_to=" that
1961  // combines the next five options (the movetype effects).
1962  } else if (apply_to == "movement_costs") {
1963  if (const config &ap = effect.child("movement_costs")) {
1964  movement_type_.get_movement().merge(ap, effect["replace"].to_bool());
1965  }
1966  } else if (apply_to == "vision_costs") {
1967  if (const config &ap = effect.child("vision_costs")) {
1968  movement_type_.get_vision().merge(ap, effect["replace"].to_bool());
1969  }
1970  } else if (apply_to == "jamming_costs") {
1971  if (const config &ap = effect.child("jamming_costs")) {
1972  movement_type_.get_jamming().merge(ap, effect["replace"].to_bool());
1973  }
1974  } else if (apply_to == "defense") {
1975  if (const config &ap = effect.child("defense")) {
1976  movement_type_.get_defense().merge(ap, effect["replace"].to_bool());
1977  }
1978  } else if (apply_to == "resistance") {
1979  if (const config &ap = effect.child("resistance")) {
1980  movement_type_.get_resistances().merge(ap, effect["replace"].to_bool());
1981  }
1982  } else if (apply_to == "zoc") {
1983  if (const config::attribute_value *v = effect.get("value")) {
1984  emit_zoc_ = v->to_bool();
1985  }
1986  } else if (apply_to == "new_ability") {
1987  if (const config &ab_effect = effect.child("abilities")) {
1988  config to_append;
1989  for (const config::any_child &ab : ab_effect.all_children_range()) {
1990  if(!has_ability_by_id(ab.cfg["id"])) {
1991  to_append.add_child(ab.key, ab.cfg);
1992  }
1993  }
1994  this->abilities_.append(to_append);
1995  }
1996  } else if (apply_to == "remove_ability") {
1997  if (const config &ab_effect = effect.child("abilities")) {
1998  for (const config::any_child &ab : ab_effect.all_children_range()) {
1999  remove_ability_by_id(ab.cfg["id"]);
2000  }
2001  }
2002  } else if (apply_to == "image_mod") {
2003  LOG_UT << "applying image_mod \n";
2004  std::string mod = effect["replace"];
2005  if (!mod.empty()){
2006  image_mods_ = mod;
2007  }
2008  LOG_UT << "applying image_mod \n";
2009  mod = effect["add"].str();
2010  if (!mod.empty()){
2011  if(!image_mods_.empty()) {
2012  image_mods_ += '~';
2013  }
2014 
2015  image_mods_ += mod;
2016  }
2017 
2019  LOG_UT << "applying image_mod \n";
2020  } else if (apply_to == "new_animation") {
2021  anim_comp_->apply_new_animation_effect(effect);
2022  } else if (apply_to == "ellipse") {
2023  set_image_ellipse(effect["ellipse"]);
2024  } else if (apply_to == "halo") {
2025  set_image_halo(effect["halo"]);
2026  } else if (apply_to == "overlay") {
2027  const std::string &add = effect["add"];
2028  const std::string &replace = effect["replace"];
2029 
2030  if (!add.empty()) {
2031  std::vector<std::string> temp_overlays = utils::parenthetical_split(add, ',');
2033  for (it=temp_overlays.begin();it<temp_overlays.end();++it) {
2034  overlays_.push_back( *it );
2035  }
2036  }
2037  else if (!replace.empty()) {
2038  overlays_ = utils::parenthetical_split(replace, ',');
2039  }
2040  } else if (apply_to == "new_advancement") {
2041  const std::string &types = effect["types"];
2042  const bool replace = effect["replace"].to_bool(false);
2043 
2044  if (!types.empty()) {
2045  if (replace) {
2047  } else {
2048  std::vector<std::string> temp_advances = utils::parenthetical_split(types, ',');
2049  std::copy(temp_advances.begin(), temp_advances.end(), std::back_inserter(advances_to_));
2050  }
2051  }
2052 
2053  if (effect.has_child("advancement")) {
2054  if (replace) {
2055  advancements_.clear();
2056  }
2057  config temp = effect;
2058  boost::copy(effect.child_range("advancement"), boost::make_function_output_iterator(ptr_vector_pushback(advancements_)));
2059  }
2060  } else if (apply_to == "remove_advancement") {
2061  const std::string &types = effect["types"];
2062  const std::string &amlas = effect["amlas"];
2063 
2064  std::vector<std::string> temp_advances = utils::parenthetical_split(types, ',');
2066  for (const std::string& unit : temp_advances) {
2067  iter = std::find(advances_to_.begin(), advances_to_.end(), unit);
2068  if (iter != advances_to_.end()) {
2069  advances_to_.erase(iter);
2070  }
2071  }
2072 
2073  temp_advances = utils::parenthetical_split(amlas, ',');
2074  std::vector<size_t> remove_indices;
2075 
2076  for(int i = advancements_.size() - 1; i >= 0; i--) {
2077  if(std::find(temp_advances.begin(), temp_advances.end(), advancements_[i]["id"]) != temp_advances.end()) {
2078  advancements_.erase(advancements_.begin() + i);
2079  }
2080  }
2081  } else if (apply_to == "alignment") {
2082  unit_type::ALIGNMENT new_align;
2083  if(new_align.parse(effect["set"])) {
2084  alignment_ = new_align;
2085  }
2086  } else if (apply_to == "max_attacks") {
2087  const std::string &increase = effect["increase"];
2088 
2089  if(!increase.empty()) {
2091  }
2092  } else if (apply_to == "recall_cost") {
2093  const std::string &increase = effect["increase"];
2094  const std::string &set = effect["set"];
2095  const int recall_cost = recall_cost_ < 0 ? resources::teams->at(side_).recall_cost() : recall_cost_;
2096 
2097  if(!set.empty()) {
2098  if(set[set.size()-1] == '%') {
2099  recall_cost_ = lexical_cast_default<int>(set)*recall_cost/100;
2100  } else {
2101  recall_cost_ = lexical_cast_default<int>(set);
2102  }
2103  }
2104 
2105  if(!increase.empty()) {
2106  recall_cost_ = utils::apply_modifier(recall_cost, increase, 1);
2107  }
2108  } else if (effect["apply_to"] == "variation") {
2109  variation_ = effect["name"].str();
2110  const unit_type * base_type = unit_types.find(type().base_id());
2111  assert(base_type != nullptr);
2112  advance_to(*base_type);
2113  } else if (effect["apply_to"] == "type") {
2114  std::string prev_type = effect["prev_type"];
2115  if (prev_type.empty()) {
2116  prev_type = type().base_id();
2117  }
2118  const std::string& new_type_id = effect["name"];
2119  const unit_type* new_type = unit_types.find(new_type_id);
2120  if (new_type) {
2121  const bool heal_full = effect["heal_full"].to_bool(false);
2122  advance_to(*new_type);
2123  preferences::encountered_units().insert(new_type_id);
2124  if(heal_full) {
2125  heal_all();
2126  }
2127  } else {
2128  WRN_UT << "unknown type= in [effect]apply_to=type, ignoring" << std::endl;
2129  }
2130  }
2131 }
2132 
2133 void unit::add_modification(const std::string& mod_type, const config& mod, bool no_add)
2134 {
2135  bool generate_description = mod["generate_description"].to_bool(true);
2136 
2137  if(no_add == false) {
2138  modifications_.add_child(mod_type, mod);
2139  }
2140  bool set_poisoned = false; // Tracks if the poisoned state was set after the type or variation was changed.
2141  config last_effect;
2142  std::vector<t_string> effects_description;
2143  for (const config &effect : mod.child_range("effect"))
2144  {
2145  // Apply SUF.
2146  if (const config &afilter = effect.child("filter")) {
2147  // @FIXME: during gamestate construction resources::filter_con is not available
2148  if (resources::filter_con && !unit_filter(vconfig(afilter), resources::filter_con).matches(*this, loc_)) continue;
2149  }
2150  const std::string &apply_to = effect["apply_to"];
2151  int times = effect["times"].to_int(1);
2152  t_string description;
2153 
2154  if (effect["times"] == "per level")
2155  times = level_;
2156 
2157  if (times) {
2158  while (times > 0) {
2159  times --;
2160 
2161  bool was_poisoned = get_state(STATE_POISONED);
2162  if (apply_to == "variation" || apply_to == "type") {
2163  // Apply unit type/variation changes last to avoid double applying effects on advance.
2164  set_poisoned = false;
2165  last_effect = effect;
2166  continue;
2167  }
2168  std::string description_component;
2169  if (resources::lua_kernel) {
2170  description_component = resources::lua_kernel->apply_effect(apply_to, *this, effect, true);
2171  } else if (builtin_effects.count(apply_to)) {
2172  // Normally, the built-in effects are dispatched through Lua so that a user
2173  // can override them if desired. However, since they're built-in, we can still
2174  // apply them if the lua kernel is unavailable.
2175  apply_builtin_effect(apply_to, effect);
2176  description_component = describe_builtin_effect(apply_to, effect);
2177  }
2178  if (!times) {
2179  description += description_component;
2180  }
2181  if (!was_poisoned && get_state(STATE_POISONED)) {
2182  set_poisoned = true;
2183  } else if(was_poisoned && !get_state(STATE_POISONED)) {
2184  set_poisoned = false;
2185  }
2186  } // end while
2187  } else { // for times = per level & level = 0 we still need to rebuild the descriptions
2188  if (resources::lua_kernel) {
2189  description += resources::lua_kernel->apply_effect(apply_to, *this, effect, false);
2190  } else if(builtin_effects.count(apply_to)) {
2191  description += describe_builtin_effect(apply_to, effect);
2192  }
2193  }
2194 
2195  if (effect["times"] == "per level" && !times) {
2196  utils::string_map symbols;
2197  symbols["effect_description"] = description;
2198  description = vgettext("$effect_description per level", symbols);
2199  }
2200  if(!description.empty())
2201  effects_description.push_back(description);
2202 
2203  }
2204  // Apply variations -- only apply if we are adding this for the first time.
2205  if (!last_effect.empty() && no_add == false) {
2206  std::string description;
2207  if (resources::lua_kernel) {
2208  description = resources::lua_kernel->apply_effect(last_effect["apply_to"], *this, last_effect, true);
2209  } else if (builtin_effects.count(last_effect["apply_to"])) {
2210  apply_builtin_effect(last_effect["apply_to"], last_effect);
2211  description = describe_builtin_effect(last_effect["apply_to"], last_effect);
2212  }
2213  effects_description.push_back(description);
2214  if ( set_poisoned )
2215  // An effect explicitly set the poisoned state, and this
2216  // should override the unit being immune to poison.
2217  set_state(STATE_POISONED, true);
2218  }
2219 
2220  t_string description;
2221 
2222  const t_string& mod_description = mod["description"];
2223  if (!mod_description.empty()) {
2224  description = mod_description + " ";
2225  }
2226 
2227  // Punctuation should be translatable: not all languages use Latin punctuation.
2228  // (However, there maybe is a better way to do it)
2229  if(effects_description.empty() == false && generate_description == true) {
2230  for(std::vector<t_string>::const_iterator i = effects_description.begin();
2231  i != effects_description.end(); ++i) {
2232  if (i->empty()) {
2233  continue;
2234  }
2235  description += *i;
2236  if(i+1 != effects_description.end())
2237  description += t_string(N_(" and "), "wesnoth");
2238  }
2239  }
2240 
2241  // store trait info
2242  if ( mod_type == "trait" ) {
2243  add_trait_description(mod, description);
2244  }
2245 
2246  //NOTE: if not a trait, description is currently not used
2247 }
2248 
2249 void unit::add_trait_description(const config& trait, const t_string& description)
2250 {
2251  const std::string& gender_string = gender_ == unit_race::FEMALE ? "female_name" : "male_name";
2252  const auto& gender_specific_name = trait[gender_string];
2253 
2254  const t_string name = gender_specific_name.empty() ? trait["name"] : gender_specific_name;
2255 
2256  if(!name.empty()) {
2257  trait_names_.push_back(name);
2258  trait_descriptions_.push_back(description);
2259  }
2260 }
2261 
2263 
2264  return type().icon().empty() ? type().image() : type().icon();
2265 }
2266 
2268  return type().image().empty() ? type().icon() : type().image();
2269 }
2270 
2272 {
2273  log_scope("apply mods");
2274 
2275  for(size_t i = 0; i != NumModificationTypes; ++i) {
2276  const std::string& mod = ModificationTypes[i];
2277  if(mod == "advance" && modifications_.has_child(mod)) {
2278  lg::wml_error() << "[modifications][advance] is deprecated, use [advancement] instead\n";
2279  }
2280  for(const config &m : modifications_.child_range(mod)) {
2281  log_scope("add mod");
2282  add_modification(ModificationTypes[i], m, true);
2283  }
2284  }
2285 
2286  //apply the experience acceleration last
2288  max_experience_ = std::max<int>(1, (max_experience_ * exp_accel + 50)/100);
2289 }
2290 
2291 bool unit::invisible(const map_location& loc, bool see_all) const
2292 {
2293  if (loc != get_location()) {
2294  DBG_UT << "unit::invisible called: id = " << id() << " loc = " << loc << " get_loc = " << get_location() << std::endl;
2295  }
2296 
2297  // This is a quick condition to check, and it does not depend on the
2298  // location (so might as well bypass the location-based cache).
2299  if ( get_state(STATE_UNCOVERED) )
2300  return false;
2301 
2302  // Fetch from cache
2303  /**
2304  * @todo FIXME: We use the cache only when using the default see_all=true
2305  * Maybe add a second cache if the see_all=false become more frequent.
2306  */
2307  if(see_all) {
2308  std::map<map_location, bool>::const_iterator itor = invisibility_cache_.find(loc);
2309  if(itor != invisibility_cache_.end()) {
2310  return itor->second;
2311  }
2312  }
2313 
2314  // Test hidden status
2315  static const std::string hides("hides");
2316  bool is_inv = get_ability_bool(hides,loc);
2317  if(is_inv){
2318  is_inv = (resources::gameboard ? !resources::gameboard->would_be_discovered(loc, side_,see_all) : true);
2319  }
2320 
2321  if(see_all) {
2322  // Add to caches
2323  if(invisibility_cache_.empty()) {
2324  units_with_cache.push_back(this);
2325  }
2326  invisibility_cache_[loc] = is_inv;
2327  }
2328 
2329  return is_inv;
2330 }
2331 
2332 
2333 bool unit::is_visible_to_team(team const& team, gamemap const& map, bool const see_all) const
2334 {
2335  map_location const& loc = get_location();
2336  if (!map.on_board(loc))
2337  return false;
2338  if (see_all)
2339  return true;
2340  if (team.is_enemy(side()) && invisible(loc))
2341  return false;
2342  // allied planned moves are also visible under fog. (we assume that fake units on the map are always whiteboard markers)
2343  if (!team.is_enemy(side()) && underlying_id_.is_fake())
2344  return true;
2345  if (team.fogged(loc))
2346  return false;
2347  return true;
2348 }
2349 
2351 {
2352  if(underlying_id_.value == 0) {
2353 
2355  underlying_id_ = id_manager.next_id();
2356  }
2357  else {
2358  underlying_id_ = id_manager.next_fake_id();
2359  }
2360  }
2361  if (id_.empty() /*&& !underlying_id_.is_fake()*/) {
2362  std::stringstream ss;
2363  ss << (type_id().empty() ? "Unit" : type_id()) << "-" << underlying_id_.value;
2364  id_ = ss.str();
2365  }
2366 }
2367 
2368 unit& unit::clone(bool is_temporary)
2369 {
2370  if(is_temporary) {
2372  } else {
2375  }
2376  else {
2378  }
2379  std::string::size_type pos = id_.find_last_of('-');
2380  if(pos != std::string::npos && pos+1 < id_.size()
2381  && id_.find_first_not_of("0123456789", pos+1) == std::string::npos) {
2382  // this appears to be a duplicate of a generic unit, so give it a new id
2383  WRN_UT << "assigning new id to clone of generic unit " << id_ << std::endl;
2384  id_.clear();
2385  set_underlying_id(resources::gameboard->unit_id_manager());
2386  }
2387  }
2388  return *this;
2389 }
2390 
2391 
2393  u_(u), moves_(u.movement_left(true))
2394 {
2395  if (operate) {
2397  }
2398 }
2399 
2401 {
2402  assert(resources::units);
2403  try {
2404 
2405  if(!resources::units->has_unit(&u_)) {
2406  /*
2407  * It might be valid that the unit is not in the unit map.
2408  * It might also mean a no longer valid unit will be assigned to.
2409  */
2410  DBG_UT << "The unit to be removed is not in the unit map.\n";
2411  }
2413 
2414  } catch (...) {}
2415 }
2416 
2417 bool unit::matches_id(const std::string& unit_id) const
2418 {
2419  return id_ == unit_id;
2420 }
2421 
2422 
2424  std::stringstream modifier;
2425  if(!flag_rgb_.empty()){
2426  modifier << "~RC("<< flag_rgb_ << ">" << team::get_side_color_index(side()) << ")";
2427  }
2428  return modifier.str();
2429 }
2431  std::stringstream modifier;
2432  if(!image_mods_.empty()){
2433  modifier << "~" << image_mods_;
2434  }
2435  modifier << TC_image_mods();
2436  return modifier.str();
2437 }
2438 
2440  return image_mods_;
2441 }
2442 
2444 {
2445  if (attacks_left_ == max_attacks_) {
2446  //TODO: add state_not_attacked
2447  }
2448  set_attacks(0);
2449 }
2450 
2452 {
2453  if (movement_left() == total_movement()) {
2454  set_state(STATE_NOT_MOVED,true);
2455  }
2456  set_movement(0, true);
2457 }
2458 
2459 
2460 void unit::set_hidden(bool state) const {
2461  hidden_ = state;
2462  if(!state) return;
2463  // We need to get rid of haloes immediately to avoid display glitches
2464  anim_comp_->clear_haloes();
2465 }
2466 
2468 {
2469  anim_comp_->clear_haloes();
2470  halo_.reset(new std::string(halo));
2471 }
2472 
2473 
2474 
2476 {
2477  if (upkeep.empty()) {
2478  return;
2479  }
2480  //TODO: create abetter way to check whether it is actually an int.
2481  int upkeep_int = upkeep.to_int(-99);
2482  if(upkeep_int != -99) {
2483  upkeep_ = upkeep_int;
2484  }
2485  else if(upkeep == "loyal" || upkeep == "free") {
2486  upkeep_ = upkeep_loyal();
2487  }
2488  else if(upkeep == "full") {
2489  upkeep_ = upkeep_full();
2490  }
2491  else {
2492  WRN_UT << "Fund invalid upkeep=\"" << upkeep << "\" in a unit\n";
2493  upkeep_ = upkeep_full();
2494  }
2495 }
2497 {
2498  if(boost::get<upkeep_full>(&upkeep_) != nullptr) {
2499  upkeep = "full";
2500  }
2501  else if(boost::get<upkeep_loyal>(&upkeep_) != nullptr) {
2502  upkeep = "loyal";
2503  }
2504  else {
2505  upkeep = boost::get<int>(upkeep_);
2506  }
2507 }
2508 
2509 // Filters unimportant stats from the unit config and returns a checksum of
2510 // the remaining config.
2512  config unit_config;
2513  config wcfg;
2514  u.write(unit_config);
2515  const std::string main_keys[] =
2516  { "advances_to",
2517  "alignment",
2518  "cost",
2519  "experience",
2520  "gender",
2521  "hitpoints",
2522  "ignore_race_traits",
2523  "ignore_global_traits",
2524  "level",
2525  "recall_cost",
2526  "max_attacks",
2527  "max_experience",
2528  "max_hitpoints",
2529  "max_moves",
2530  "movement",
2531  "movement_type",
2532  "race",
2533  "random_traits",
2534  "resting",
2535  "undead_variation",
2536  "upkeep",
2537  "zoc",
2538  ""};
2539 
2540  for (int i = 0; !main_keys[i].empty(); ++i)
2541  {
2542  wcfg[main_keys[i]] = unit_config[main_keys[i]];
2543  }
2544  const std::string attack_keys[] =
2545  { "name",
2546  "type",
2547  "range",
2548  "damage",
2549  "number",
2550  ""};
2551 
2552  for (const config &att : unit_config.child_range("attack"))
2553  {
2554  config& child = wcfg.add_child("attack");
2555  for (int i = 0; !attack_keys[i].empty(); ++i) {
2556  child[attack_keys[i]] = att[attack_keys[i]];
2557  }
2558  for (const config &spec : att.child_range("specials")) {
2559  config& child_spec = child.add_child("specials", spec);
2560  child_spec.recursive_clear_value("description");
2561  }
2562 
2563  }
2564 
2565  for (const config &abi : unit_config.child_range("abilities"))
2566  {
2567  config& child = wcfg.add_child("abilities", abi);
2568  child.recursive_clear_value("description");
2569  child.recursive_clear_value("description_inactive");
2570  child.recursive_clear_value("name");
2571  child.recursive_clear_value("name_inactive");
2572  }
2573 
2574  for (const config &trait : unit_config.child_range("trait"))
2575  {
2576  config& child = wcfg.add_child("trait", trait);
2577  child.recursive_clear_value("description");
2578  child.recursive_clear_value("male_name");
2579  child.recursive_clear_value("female_name");
2580  child.recursive_clear_value("name");
2581  }
2582 
2583  const std::string child_keys[] = {"advance_from", "defense", "movement_costs", "vision_costs", "jamming_costs", "resistance", ""};
2584 
2585  for (int i = 0; !child_keys[i].empty(); ++i)
2586  {
2587  for (const config &c : unit_config.child_range(child_keys[i])) {
2588  wcfg.add_child(child_keys[i], c);
2589  }
2590  }
2591  DBG_UT << wcfg;
2592 
2593  return wcfg.hash();
2594 }
2595 
std::string image_mods() const
Definition: unit.cpp:2430
static unit_id create_real(size_t val)
Definition: id.hpp:35
unit_id next_fake_id()
Definition: id.cpp:37
child_itors child_range(const std::string &key)
Definition: config.cpp:613
void heal(int amount)
Definition: unit.cpp:1253
static const std::set< std::string > builtin_effects
Definition: unit.hpp:342
int total_movement() const
Definition: unit.hpp:218
void generate_traits(bool musthaveonly=false)
Apply mandatory traits (e.g.
Definition: unit.cpp:827
const unit_type * type_
Definition: unit.hpp:443
const std::string & id() const
Definition: unit.hpp:148
t_string unit_description() const
Information about the unit – a detailed description of it.
Definition: unit.hpp:166
bool is_visible_to_team(team const &team, gamemap const &map, bool const see_all=true) const
Definition: unit.cpp:2333
t_string type_name_
Never nullptr. Adjusted for gender and variation.
Definition: unit.hpp:444
std::string apply_effect(const std::string &name, unit &u, const config &cfg, bool need_apply)
static DIRECTION parse_direction(const std::string &str)
Definition: location.cpp:69
double hp_bar_scaling
std::vector< bool > known_boolean_states_
Definition: unit.hpp:488
void apply_builtin_effect(std::string type, const config &effect)
Definition: unit.cpp:1800
const map_location & get_location() const
Definition: unit.hpp:286
std::vector< std::string > recruit_list_
Definition: unit.hpp:461
std::string const & gender_string(unit_race::GENDER gender)
Definition: race.cpp:151
static int get_acceleration()
Definition: types.cpp:509
void set_usage(const std::string &usage)
Definition: unit.hpp:364
resistances & get_resistances()
Definition: movetype.hpp:185
void set_hidden(bool state) const
Definition: unit.cpp:2460
std::string absolute_image() const
The name of the file to game_display (used in menus).
Definition: unit.cpp:2262
const std::string log_id() const
A variant on id() that is more descriptive, for use with message logging.
Definition: types.hpp:117
ptr_vector_pushback(boost::ptr_vector< config > &vec)
Definition: unit.cpp:325
config modifications_
Definition: unit.hpp:523
int recall_cost_
Definition: unit.hpp:459
utils::string_map modification_descriptions_
Definition: unit.hpp:512
double xp_bar_scaling
all_children_iterator ordered_end() const
Definition: config.cpp:1122
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
Definition: cursor.cpp:154
void generate_name()
Definition: unit.cpp:804
unit_movement_resetter(unit &u, bool operate=true)
Definition: unit.cpp:2392
const unit_type & get_gender_unit_type(std::string gender) const
Definition: types.cpp:439
std::pair< const_attribute_iterator, const_attribute_iterator > const_attr_itors
Definition: config.hpp:418
int max_hitpoints() const
Definition: unit.hpp:169
const t_string & type_name() const
The name of the unit in the current language setting.
Definition: types.hpp:112
bool get_state(const std::string &state) const
Definition: unit.cpp:1289
void parse_upkeep(const config::attribute_value &upkeep)
Definition: unit.cpp:2475
std::string id_
Definition: formula.cpp:636
Definition: unit.hpp:95
size_t underlying_id() const
The unique internal ID of the unit.
Definition: unit.hpp:150
int movement() const
Definition: types.hpp:128
void intrusive_ptr_release(const unit *u)
Definition: unit.cpp:180
std::string TC_image_mods() const
Definition: unit.cpp:2423
void append(const config &cfg)
Append data from another config object to this one.
Definition: config.cpp:566
int experience() const
Definition: unit.hpp:171
std::string image_mods_
Definition: unit.hpp:464
std::vector< std::string > advances_to_
Definition: unit.hpp:442
const GLfloat * c
Definition: glew.h:12741
int pos
Definition: formula.cpp:800
t_string name_
Definition: unit.hpp:447
unit_id next_id()
returns id for unit that is created
Definition: id.cpp:30
void new_scenario()
Definition: unit.cpp:1238
iterator erase(const iterator &erase_it)
Definition: unit.hpp:86
std::vector< config > get_modification_advances() const
Definition: unit.cpp:1630
fixed_t alpha_
Definition: unit.hpp:472
int max_attacks() const
Definition: types.hpp:133
std::vector< t_string > trait_names_
Definition: unit.hpp:504
void remove_movement_ai()
Definition: unit.cpp:2451
int vision_
Definition: unit.hpp:478
vconfig child(const std::string &key) const
Returns a child of *this whose key is key.
Definition: variable.cpp:243
rng * generator
This generator is automatically synced during synced context.
Definition: random_new.cpp:52
GLuint GLuint GLsizei GLenum type
Definition: glew.h:1221
int hitpoints() const
Definition: unit.hpp:168
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:29
bool matches_id(const std::string &unit_id) const
Definition: unit.cpp:2417
int upkeep() const
Definition: unit.cpp:1498
config events_
Definition: unit.hpp:491
void set_image_halo(const std::string &halo)
Definition: unit.cpp:2467
unit & operator=(unit)
Assignment operator.
Definition: unit.cpp:797
unit_type::ALIGNMENT alignment_
Definition: unit.hpp:462
bool is_fearless_
Definition: unit.hpp:510
const movetype & movement_type() const
Definition: types.hpp:150
void intrusive_ptr_add_ref(const unit *u)
Intrusive Pointer interface.
Definition: unit.cpp:164
bool is_enemy(int n) const
Definition: team.hpp:247
map_location::DIRECTION facing_
Definition: unit.hpp:501
double xp_bar_scaling() const
Definition: types.hpp:125
std::string flag_rgb_
Definition: unit.hpp:463
int jamming_
Definition: unit.hpp:479
int unit_value_
Definition: unit.hpp:507
attribute_map::value_type attribute
Definition: config.hpp:393
Definition: id.cpp:23
const std::vector< std::string > advances_to_translated() const
Definition: unit.cpp:1121
fixed_t alpha() const
Definition: types.hpp:164
void set_image_ellipse(const std::string &ellipse)
Definition: unit.hpp:363
const std::string & undead_variation() const
Info on the type of unit that the unit reanimates as.
Definition: types.hpp:107
bool generate_name_
Definition: unit.hpp:531
GLenum GLsizei GLenum GLenum const GLvoid * image
Definition: glew.h:3783
bool hold_position_
Definition: unit.hpp:481
const std::string & id() const
Definition: race.hpp:33
bool resistance_filter_matches(const config &cfg, bool attacker, const std::string &damage_name, int res) const
Definition: unit.cpp:1535
int jamming() const
Definition: types.hpp:132
void advance_to(const unit_type &t, bool use_traits=false)
Advances this unit to another type.
Definition: unit.cpp:904
unit_type_data unit_types
Definition: types.cpp:1314
const unit_race * race() const
Never returns nullptr, but may point to the null race.
Definition: types.hpp:218
const std::string & image() const
Definition: types.hpp:138
config variables_
Definition: unit.hpp:490
void merge(const config &new_data, bool overwrite)
Merges the given config over the existing costs.
Definition: movetype.cpp:664
void write(config &cfg) const
Writes the movement type data to the provided config.
Definition: movetype.cpp:792
const config & abilities_cfg() const
Definition: types.hpp:185
std::pair< int, map_location > highest(const std::string &key, int def=0) const
Definition: abilities.cpp:426
int side() const
Definition: unit.hpp:201
void write(config &cfg) const
Definition: unit.cpp:1379
SDL_Color xp_color() const
Colors for the unit's XP.
Definition: unit.cpp:1075
void clear()
Definition: config.cpp:1055
void write_upkeep(config::attribute_value &upkeep) const
Definition: unit.cpp:2496
void set_state(const std::string &state, bool value)
Definition: unit.cpp:1337
const config & child_or_empty(const std::string &key) const
Returns the first child with the given key, or an empty config if there is none.
Definition: config.cpp:722
bool generate_name() const
Definition: types.hpp:144
void merge(const config &new_cfg, bool overwrite=true)
Merges the given config over the existing data.
Definition: movetype.cpp:754
bool is_healthy_
Definition: unit.hpp:510
bool get_ability_bool(const std::string &tag_name, const map_location &loc) const
Returns true if the unit is currently under effect by an ability with this given TAG NAME...
Definition: abilities.cpp:129
-file sdl_utils.hpp
int level() const
Definition: types.hpp:126
void clear_children(const std::string &key)
Definition: config.cpp:820
bool empty() const
Definition: config.cpp:1105
To lexical_cast(From value)
Lexical cast converts one type to another.
bool empty() const
Tests for an attribute that either was never set or was set to "".
Definition: config.cpp:375
void expire_modifications(const std::string &duration)
Clears those modifications whose duration has expired.
Definition: unit.cpp:1180
std::string ellipse() const
Definition: types.hpp:143
GLdouble GLdouble t
Definition: glew.h:1366
int max_hit_points_
Definition: unit.hpp:453
std::string small_profile() const
The unit's profile.
Definition: unit.cpp:1020
bool is_fake() const
Definition: id.hpp:32
GLsizei GLenum GLenum * types
Definition: glew.h:3155
void operator()(const config &cfg)
Definition: unit.cpp:326
std::pair< const_child_iterator, const_child_iterator > const_child_itors
Definition: config.hpp:214
Variant for storing WML attributes.
Definition: config.hpp:223
int defense_modifier(const t_translation::t_terrain &terrain) const
Definition: unit.cpp:1520
std::string small_profile_
Definition: unit.hpp:534
game_data * gamedata
Definition: resources.cpp:22
static std::map< std::string, state_t > known_boolean_state_names_
Definition: unit.hpp:489
map_location goto_
Definition: unit.hpp:508
bool filter_base_matches(const config &cfg, int def)
Definition: abilities.cpp:992
unit(const unit &u)
Definition: unit.cpp:246
std::string describe_builtin_effect(std::string type, const config &effect)
Definition: unit.cpp:1720
std::vector< unit_ability >::iterator iterator
Definition: unit.hpp:72
terrain_costs & get_jamming()
Definition: movetype.hpp:183
const std::vector< std::string > & recruits() const
Definition: unit.hpp:209
map_location loc_
t_advancements advancements_
Definition: unit.hpp:525
void apply_modifications()
Definition: unit.cpp:2271
const unit_type & type() const
The type of the unit (accounting for gender and variation).
Definition: unit.hpp:144
int get_random_int(int min, int max)
This helper method provides a random int from the underlying generator, using results of next_random...
Definition: random_new.hpp:50
std::map< std::string, std::string > advancement_icons() const
Definition: unit.cpp:1580
bool has_child(const std::string &key) const
Determine whether a config has a child or not.
Definition: config.cpp:651
const std::string & type_id() const
The id of the type of the unit.
Definition: unit.hpp:142
static void clear_status_caches()
Clear the unit status cache for all units.
Definition: unit.cpp:610
std::string role_
Definition: unit.hpp:498
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:50
#define LOG_UT
Definition: unit.cpp:78
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:57
t_upkeep upkeep_
Definition: unit.hpp:532
static const std::string & leader_crown()
The path to the leader crown overlay.
Definition: unit.cpp:234
static lg::log_domain log_enginerefac("enginerefac")
void set_facing(map_location::DIRECTION dir) const
Definition: unit.cpp:1491
const unit_race * find_race(const std::string &) const
Definition: types.cpp:1286
const std::string & flag_rgb() const
Definition: types.hpp:147
GLuint GLuint end
Definition: glew.h:1221
bool null() const
Definition: variable.hpp:66
GLuint64EXT * result
Definition: glew.h:10727
std::map< std::string, t_string > string_map
void swap(unit &)
Swap, for copy and swap idiom.
Definition: unit.cpp:728
std::vector< team > * teams
Definition: resources.cpp:29
static const unit_race null_race
Dummy race used when a race is not yet known.
Definition: race.hpp:49
child_list get_children(const std::string &key) const
Definition: variable.cpp:181
const std::string & big_profile() const
Definition: types.hpp:141
int max_movement_
Definition: unit.hpp:477
std::string profile_
Definition: unit.hpp:533
bool matches(const unit &u, const map_location &loc) const
Determine if *this matches filter at a specified location.
Definition: filter.hpp:67
filter_context * filter_con
Definition: resources.cpp:23
void add_color_info(const config &v)
const GLdouble * v
Definition: glew.h:1359
GLsizei const GLfloat * value
Definition: glew.h:1817
all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:1127
void heal_all()
Definition: unit.hpp:241
terrain_defense & get_defense()
Definition: movetype.hpp:184
terrain_costs & get_vision()
Definition: movetype.hpp:182
game_board * gameboard
Definition: resources.cpp:20
void set_recruits(const std::vector< std::string > &recruits)
Definition: unit.cpp:1112
GLboolean GLboolean GLboolean GLboolean a
Definition: glew.h:7319
size_t value
Definition: id.hpp:30
void add_trait_description(const config &trait, const t_string &description)
register a trait's name and its description for UI's use
Definition: unit.cpp:2249
#define WRN_UT
Definition: unit.cpp:79
Encapsulates the map of the game.
Definition: map.hpp:37
void remove_child(const std::string &key, unsigned index)
Definition: config.cpp:899
state_t
Definition: unit.hpp:248
static UNUSEDNOWARN std::string _n(const char *str1, const char *str2, int n)
Definition: gettext.hpp:86
const std::vector< unit_race::GENDER > & genders() const
The returned vector will not be empty, provided this has been built to the HELP_INDEXED status...
Definition: types.hpp:198
bool can_recruit() const
Definition: unit.hpp:207
unit_race::GENDER string_gender(const std::string &str, unit_race::GENDER def)
Definition: race.cpp:161
int kill_experience
Definition: game_config.cpp:43
GLuint num
Definition: glew.h:2552
config & add_child(const std::string &key)
Definition: config.cpp:743
const t_advancements & modification_advancements() const
Definition: unit.hpp:333
int max_experience_
Definition: unit.hpp:455
const std::string & icon() const
Definition: types.hpp:139
bool mod_duration_match(const std::string &mod_dur, const std::string &goal_dur)
Determines if mod_dur "matches" goal_dur.
Definition: unit.cpp:1164
unit_race::GENDER gender_
Definition: unit.hpp:470
boost::scoped_ptr< unit_animation_component > anim_comp_
Definition: unit.hpp:517
int hit_points_
Definition: unit.hpp:452
int movement_left() const
Returns how far a unit can move this turn (zero if incapacitated).
Definition: unit.hpp:220
static const ::config * terrain
The terrain used to create the cache.
Definition: minimap.cpp:135
void merge(const config &new_data, bool overwrite)
Merges the given config over the existing costs.
Definition: movetype.hpp:138
boost::scoped_ptr< std::string > halo_
Definition: unit.hpp:528
const std::string & small_profile() const
Definition: types.hpp:140
void check_types(const std::vector< std::string > &types) const
Definition: types.cpp:1177
game_events::manager * game_events
Definition: resources.cpp:24
bool random_traits_
Definition: unit.hpp:530
terrain_costs & get_movement()
Definition: movetype.hpp:181
GLuint GLuint GLsizei count
Definition: glew.h:1221
all_children_iterator erase(const all_children_iterator &i)
Definition: config.cpp:894
GLuint color
Definition: glew.h:5801
long ref_count() const
Definition: unit.hpp:414
bool invisible(const map_location &loc, bool see_all=true) const
Definition: unit.cpp:2291
const std::string &parameters float amount
Definition: filter.cpp:132
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:47
std::string join(T const &v, const std::string &s=",")
Generates a new string joining container items in a list.
int cost() const
Definition: types.hpp:134
boost::scoped_ptr< std::string > usage_
Definition: unit.hpp:527
config abilities_
Definition: unit.hpp:524
std::vector< std::string > overlays_
Definition: unit.hpp:496
Encapsulates the map of the game.
Definition: location.hpp:38
static lg::log_domain log_config("config")
config::const_child_itors advancements() const
Definition: types.hpp:188
bool hidden_
Definition: unit.hpp:520
std::vector< std::pair< std::string, std::string > > amla_icons() const
Definition: unit.cpp:1611
std::string default_anim_image() const
The default image to use for animation frames with no defined image.
Definition: unit.cpp:2267
bool empty() const
Definition: unit.hpp:80
#define DBG_UT
Definition: unit.cpp:77
const std::string & usage() const
Definition: types.hpp:137
movetype movement_type_
Definition: unit.hpp:480
const std::vector< std::string > & advances_to() const
Definition: types.hpp:97
GLuint res
Definition: glew.h:9258
config filter_recall_
Definition: unit.hpp:492
void add_modification(const std::string &type, const config &modification, bool no_add=false)
Definition: unit.cpp:2133
double hp_bar_scaling() const
Definition: types.hpp:124
const std::vector< std::string > & advances_to() const
Definition: unit.hpp:133
int max_attacks_
Definition: unit.hpp:485
bool getsHit_
Definition: unit.hpp:519
unit & clone(bool is_temporary=true)
Mark this unit as clone so it can be inserted to unit_map.
Definition: unit.cpp:2368
std::map< std::string, tfilter >::iterator itor
Definition: filter.cpp:199
std::stringstream & wml_error()
Use this logger to send errors due to deprecated WML.
Definition: log.cpp:262
void end_turn()
Definition: unit.cpp:1225
bool has_zoc() const
Definition: types.hpp:177
int resistance_against(const attack_type &attack) const
Returns the resistance against the indicated attack.
Definition: movetype.hpp:213
void remove_attacks_ai()
Definition: unit.cpp:2443
std::string get_checksum(const unit &u)
Gets a checksum for a unit.
Definition: unit.cpp:2511
std::string halo() const
Definition: types.hpp:142
config & variables()
Definition: unit.hpp:366
const unit_type & get_variation(const std::string &id) const
Definition: types.cpp:457
const std::map< std::string, std::string > get_states() const
Definition: unit.cpp:1267
size_t modification_count(const std::string &type, const std::string &id) const
Definition: unit.cpp:1695
config::const_child_itors possible_traits() const
Definition: types.hpp:182
const config & get_config() const
Definition: variable.hpp:68
const_attr_itors attribute_range() const
Definition: config.cpp:984
double hp_bar_scaling_
Definition: unit.hpp:521
map_location interrupted_move_
Definition: unit.hpp:508
boost::scoped_ptr< unit_formula_manager > formula_man_
Definition: unit.hpp:474
iterator begin()
Definition: unit.hpp:74
int vision() const
Definition: types.hpp:129
bool canrecruit_
Definition: unit.hpp:460
void set_interrupted_move(const map_location &interrupted_move)
Definition: unit.hpp:348
void new_turn()
Definition: unit.cpp:1215
int experience_
Definition: unit.hpp:454
#define log_scope(description)
Definition: log.hpp:185
size_t i
Definition: function.cpp:1057
void set_movement(int moves, bool unit_action=false)
Set the unit's remaining movement to moves.
Definition: unit.cpp:1148
n_unit::unit_id underlying_id_
Definition: unit.hpp:448
double xp_bar_scaling_
Definition: unit.hpp:521
bool loyal() const
Definition: unit.cpp:1515
DIRECTION
Valid directions which can be moved in our hexagonal world.
Definition: location.hpp:40
bool fogged(const map_location &loc) const
Definition: team.cpp:575
std::string generate_name(GENDER gender) const
Definition: race.cpp:123
int level() const
Definition: unit.hpp:175
void validate_side(int side)
Definition: team.cpp:657
t_string description_
Definition: unit.hpp:526
std::set< std::string > & encountered_units()
GLdouble GLdouble GLdouble r
Definition: glew.h:1374
int attacks_left_
Definition: unit.hpp:484
#define N_(String)
Definition: gettext.hpp:90
static int sort(lua_State *L)
Definition: ltablib.cpp:246
unit_ability_list get_abilities(const std::string &tag_name, const map_location &loc) const
Definition: abilities.cpp:168
unsigned child_count(const std::string &key) const
Definition: config.cpp:635
int resistance_against(const std::string &damage_name, bool attacker, const map_location &loc) const
Definition: unit.cpp:1559
std::string big_profile() const
Definition: unit.cpp:1012
std::string vgettext(const char *msgid, const utils::string_map &symbols)
Handling of system events.
Definition: manager.hpp:42
std::string replace(std::string str, const std::string &src, const std::string &dst)
Replace all instances of src in str with dst.
GLuint const GLchar * name
Definition: glew.h:1782
int hitpoints() const
Definition: types.hpp:123
const attribute_value * get(const std::string &key) const
Returns a pointer to the attribute with the given key or nullptr if it does not exist.
Definition: config.cpp:935
long ref_count_
Definition: unit.hpp:418
int experience_needed(bool with_acceleration=true) const
Definition: types.cpp:514
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:467
config::const_child_itors events() const
Definition: types.hpp:191
std::string hash() const
Definition: config.cpp:1468
GLsizeiptr size
Definition: glew.h:1649
static const unit_type & get_unit_type(const std::string &type_id)
Converts a string ID to a unit_type.
Definition: unit.cpp:196
const std::string & effect_image_mods() const
Definition: unit.cpp:2439
const std::string & base_id() const
The id of the original type from which this (variation) descended.
Definition: types.hpp:119
Definition: display.hpp:43
std::vector< std::string > parenthetical_split(std::string const &val, const char separator, std::string const &left, std::string const &right, const int flags)
Splits a string based either on a separator where text within parenthesis is protected from splitting...
static unit_race::GENDER generate_gender(const unit_type &type, bool random_gender)
Definition: unit.cpp:208
bool can_advance() const
Definition: unit.hpp:323
GLclampd n
Definition: glew.h:5903
void set_advancements(std::vector< config > advancements)
Definition: unit.cpp:1685
int movement_
Definition: unit.hpp:476
n_unit::id_manager & unit_id_manager()
Definition: game_board.hpp:91
int max_experience() const
Definition: unit.hpp:172
const GLdouble * m
Definition: glew.h:6968
static state_t get_known_boolean_state_id(const std::string &state)
Definition: unit.cpp:1314
int recall_cost() const
Definition: unit.hpp:177
SDL_Color hp_color() const
Colors for the unit's current hitpoints.
Definition: unit.cpp:1065
boost::scoped_ptr< std::string > ellipse_
Definition: unit.hpp:529
bool find(E event, F functor)
Tests whether an event handler is available.
int apply_modifier(const int number, const std::string &amount, const int minimum)
bool unrenamable_
Definition: unit.hpp:466
int level_
Definition: unit.hpp:457
std::vector< attack_type > attacks_
Definition: unit.hpp:499
void swap(game_board &one, game_board &other)
Definition: game_board.cpp:56
config & child(const std::string &key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:658
std::vector< attack_type > attacks() const
Definition: types.cpp:484
A variable-expanding proxy for the config class.
Definition: variable.hpp:36
this module manages the cache of images.
Definition: image.cpp:75
void adjust_profile(std::string &profile)
Definition: types.cpp:1317
Standard logging facilities (interface).
const unit_race * race_
The displayed name of the unit type.
Definition: unit.hpp:445
std::string variation_
Definition: unit.hpp:450
static void check_id(std::string &id)
Definition: types.cpp:1292
bool emit_zoc_
Definition: unit.hpp:494
game_lua_kernel * lua_kernel
Definition: resources.cpp:25
void recursive_clear_value(const std::string &key)
Definition: config.cpp:860
std::map< map_location, bool > invisibility_cache_
Hold the visibility status cache for a unit, when not uncovered.
Definition: unit.hpp:542
void set_attacks(int left)
Definition: unit.hpp:233
static std::map< std::string, state_t > get_known_boolean_state_names()
Definition: unit.cpp:1324
int to_int(int def=0) const
Definition: config.cpp:308
static std::string get_side_color_index(int side)
Definition: team.cpp:840
unsigned int num_traits() const
Definition: types.hpp:109
const unit_type * find(const std::string &key, unit_type::BUILD_STATUS status=unit_type::FULL) const
Finds a unit_type by its id() and makes sure it is built to the specified level.
Definition: types.cpp:1155
const map_location goto_
Definition: move.cpp:305
std::string id_
Never nullptr, but may point to the null race.
Definition: unit.hpp:446
std::vector< std::string > split(std::string const &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
static SDL_Color hp_color_(int hitpoints, int max_hitpoints)
Definition: unit.cpp:1028
#define e
std::vector< t_string > trait_descriptions_
Definition: unit.hpp:505
boost::ptr_vector< config > * vec_
Definition: unit.cpp:331
int get_composite_value() const
Definition: abilities.hpp:50
bool resting_
Definition: unit.hpp:483
int recall_cost() const
Definition: types.hpp:127
std::vector< vconfig > child_list
Definition: variable.hpp:71
bool has_ability_by_id(const std::string &ability) const
Definition: unit.cpp:1357
#define ERR_UT
Definition: unit.cpp:80
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
map_location loc_
Definition: unit.hpp:440
void remove_ability_by_id(const std::string &ability)
Definition: unit.cpp:1367
GLdouble s
Definition: glew.h:1358
static lg::log_domain log_engine("engine")
virtual ~unit()
Definition: unit.cpp:708
std::string print_modifier(const std::string &mod)
void set_underlying_id(n_unit::id_manager &id_manager)
Definition: unit.cpp:2350
static std::string write_direction(DIRECTION dir)
Definition: location.cpp:144
bool would_be_discovered(const map_location &loc, int side_num, bool see_all=true)
Given a location and a side number, indicates whether an invisible unit of that side at that location...
Defines the MAKE_ENUM macro.
static bool is_synced()
void add_events(const config::const_child_itors &cfgs, const std::string &type=std::string())
Definition: manager.cpp:165
GLsizei const GLcharARB ** string
Definition: glew.h:4503
int side_
Definition: unit.hpp:468
static lg::log_domain log_unit("unit")
unit_map * units
Definition: resources.cpp:35
void merge(const config &new_values, bool overwrite)
Merges the given config over the existing values.
Definition: movetype.cpp:511
bool empty() const
Definition: tstring.hpp:166
iterator end()
Definition: unit.hpp:76
const std::string & id() const
The id for this unit_type.
Definition: types.hpp:115
int defense_modifier(const t_translation::t_terrain &terrain) const
Returns the defensive value of the indicated terrain.
Definition: movetype.hpp:209
bool end_turn_
Definition: unit.hpp:482
std::set< std::string > states_
Definition: unit.hpp:487
void set_advances_to(const std::vector< std::string > &advances_to)
Definition: unit.cpp:1136
t_string unit_description() const
Definition: types.cpp:467
const config & get_cfg_for_units() const
Returns a trimmed config suitable for use with units.
Definition: types.hpp:224
all_children_iterator ordered_begin() const
Definition: config.cpp:1117
std::string undead_variation_
Definition: unit.hpp:449
std::vector< std::string > get_traits_list() const
Definition: unit.cpp:883