The Battle for Wesnoth  1.15.0-dev
reports.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
3  Part of the Battle for Wesnoth Project https://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 #include "actions/attack.hpp"
16 #include "attack_prediction.hpp"
17 #include "font/pango/escape.hpp"
18 #include "font/text_formatting.hpp"
19 #include "formatter.hpp"
20 #include "preferences/game.hpp"
21 #include "gettext.hpp"
22 #include "language.hpp"
23 #include "map/map.hpp"
24 #include "mouse_events.hpp"
25 #include "reports.hpp"
26 #include "color.hpp"
27 #include "team.hpp"
28 #include "terrain/movement.hpp"
29 #include "tod_manager.hpp"
30 #include "units/unit.hpp"
31 #include "units/helper.hpp"
32 #include "whiteboard/manager.hpp"
33 
34 #include <cassert>
35 #include <ctime>
36 #include <iomanip>
37 #include <boost/dynamic_bitset.hpp>
38 
39 static void add_text(config &report, const std::string &text,
40  const std::string &tooltip, const std::string &help = "")
41 {
42  config &element = report.add_child("element");
43  element["text"] = text;
44  if (!tooltip.empty()) element["tooltip"] = tooltip;
45  if (!help.empty()) element["help"] = help;
46 }
47 
48 static void add_image(config &report, const std::string &image,
49  const std::string &tooltip, const std::string &help = "")
50 {
51  config &element = report.add_child("element");
52  element["image"] = image;
53  if (!tooltip.empty()) element["tooltip"] = tooltip;
54  if (!help.empty()) element["help"] = help;
55 }
56 
57 static config text_report(const std::string &text,
58  const std::string &tooltip = "", const std::string &help = "")
59 {
60  config r;
61  add_text(r, text, tooltip, help);
62  return r;
63 }
64 
65 static config image_report(const std::string &image,
66  const std::string &tooltip = "", const std::string &help = "")
67 {
68  config r;
69  add_image(r, image, tooltip, help);
70  return r;
71 }
72 
73 using font::span_color;
74 
75 static void add_status(config &r,
76  char const *path, char const *desc1, char const *desc2)
77 {
78  std::ostringstream s;
79  s << translation::gettext(desc1) << translation::gettext(desc2);
80  add_image(r, path, s.str());
81 }
82 
83 static std::string flush(std::ostringstream &s)
84 {
85  std::string r(s.str());
86  s.str(std::string());
87  return r;
88 }
89 
90 typedef std::map<std::string, reports::generator_function> static_report_generators;
92 
94 {
96  {
97  static_generators.insert(static_report_generators::value_type(name, g));
98  }
99 };
100 
101 #define REPORT_GENERATOR(n, cn) \
102  static config report_##n(reports::context & cn); \
103  static report_generator_helper reg_gen_##n(#n, &report_##n); \
104  static config report_##n(reports::context & cn)
105 
106 static char const *naps = "</span>";
107 
109 {
110  return rc.dc().get_visible_unit(rc.screen().displayed_unit_hex(),
111  rc.teams()[rc.screen().viewing_team()],
112  rc.screen().show_everything());
113 }
114 
116 {
117  return rc.dc().get_visible_unit(rc.screen().selected_hex(),
118  rc.teams()[rc.screen().viewing_team()],
119  rc.screen().show_everything());
120 }
121 
122 static config gray_inactive(reports::context & rc, const std::string &str, const std::string& tooltip = "")
123 {
124  if ( rc.screen().viewing_side() == rc.screen().playing_side() )
125  return text_report(str, tooltip);
126 
127  return text_report(span_color(font::GRAY_COLOR) + str + naps, tooltip);
128 }
129 
130 static config unit_name(const unit *u)
131 {
132  if (!u) {
133  return config();
134  }
135 
136  /*
137  * The name needs to be escaped, it might be set by the user and using
138  * markup. Also names often contain a forbidden single quote.
139  */
140  const std::string& name = font::escape_text(u->name());
141  std::ostringstream str, tooltip;
142  str << "<b>" << name << "</b>";
143  tooltip << _("Name: ") << "<b>" << name << "</b>";
144  return text_report(str.str(), tooltip.str());
145 }
146 
148 {
149  const unit *u = get_visible_unit(rc);
150  return unit_name(u);
151 }
152 REPORT_GENERATOR(selected_unit_name, rc)
153 {
154  const unit *u = get_selected_unit(rc);
155  return unit_name(u);
156 }
157 
158 static config unit_type(const unit* u)
159 {
160  if (!u) return config();
161  std::string has_variations_prefix = (u->type().show_variations_in_help() ? ".." : "");
162  std::ostringstream str, tooltip;
163  str << u->type_name();
164  tooltip << _("Type: ") << "<b>" << u->type_name() << "</b>\n"
165  << u->unit_description();
166  return text_report(str.str(), tooltip.str(), has_variations_prefix + "unit_" + u->type_id());
167 }
169 {
170  const unit *u = get_visible_unit(rc);
171  return unit_type(u);
172 }
173 REPORT_GENERATOR(selected_unit_type, rc)
174 {
175  const unit *u = get_selected_unit(rc);
176  return unit_type(u);
177 }
178 
179 static config unit_race(const unit* u)
180 {
181  if (!u) return config();
182  std::ostringstream str, tooltip;
183  str << u->race()->name(u->gender());
184  tooltip << _("Race: ") << "<b>" << u->race()->name(u->gender()) << "</b>";
185  return text_report(str.str(), tooltip.str(), "..race_" + u->race()->id());
186 }
188 {
189  const unit *u = get_visible_unit(rc);
190  return unit_race(u);
191 }
192 REPORT_GENERATOR(selected_unit_race, rc)
193 {
194  const unit *u = get_selected_unit(rc);
195  return unit_race(u);
196 }
197 
198 static config unit_side(reports::context & rc, const unit* u)
199 {
200  if (!u) return config();
201 
202  config report;
203  const team &u_team = rc.dc().get_team(u->side());
204  std::string flag_icon = u_team.flag_icon();
205  std::string old_rgb = game_config::flag_rgb;
206  std::string new_rgb = u_team.color();
207  std::string mods = "~RC(" + old_rgb + ">" + new_rgb + ")";
208  if (flag_icon.empty())
209  flag_icon = game_config::images::flag_icon;
210 
211  std::stringstream text;
212  text << " " << u->side();
213 
214  add_image(report, flag_icon + mods, u_team.side_name(), "");
215  add_text(report, text.str(), "", "");
216  return report;
217 }
219 {
220  const unit *u = get_visible_unit(rc);
221  return unit_side(rc,u);
222 }
223 REPORT_GENERATOR(selected_unit_side, rc)
224 {
225  const unit *u = get_selected_unit(rc);
226  return unit_side(rc, u);
227 }
228 
229 static config unit_level(const unit* u)
230 {
231  if (!u) return config();
232  std::ostringstream str, tooltip;
233  str << u->level();
234  tooltip << _("Level: ") << "<b>" << u->level() << "</b>\n";
235  const std::vector<std::string> &adv_to = u->advances_to_translated();
236  if (adv_to.empty())
237  tooltip << _("No advancement");
238  else
239  tooltip << _("Advances to:") << "\n<b>\t"
240  << utils::join(adv_to, "\n\t") << "</b>";
241  return text_report(str.str(), tooltip.str());
242 }
244 {
245  const unit *u = get_visible_unit(rc);
246  return unit_level(u);
247 }
248 REPORT_GENERATOR(selected_unit_level, rc)
249 {
250  const unit *u = get_selected_unit(rc);
251  return unit_level(u);
252 }
253 
254 REPORT_GENERATOR(unit_amla, rc)
255 {
256  const unit *u = get_visible_unit(rc);
257  if (!u) return config();
258  config res;
259  typedef std::pair<std::string, std::string> pair_string;
260  for (const pair_string &ps : u->amla_icons()) {
261  add_image(res, ps.first, ps.second);
262  }
263  return res;
264 }
265 
266 static config unit_traits(const unit* u)
267 {
268  if (!u) return config();
269  config res;
270  const std::vector<t_string> &traits = u->trait_names();
271  const std::vector<t_string> &descriptions = u->trait_descriptions();
272  const std::vector<std::string> &trait_ids = u->get_traits_list();
273  unsigned nb = traits.size();
274  for (unsigned i = 0; i < nb; ++i)
275  {
276  std::ostringstream str, tooltip;
277  str << traits[i];
278  if (i != nb - 1 ) str << ", ";
279  tooltip << _("Trait: ") << "<b>" << traits[i] << "</b>\n"
280  << descriptions[i];
281  add_text(res, str.str(), tooltip.str(), "traits_" + trait_ids[i]);
282  }
283  return res;
284 }
286 {
287  const unit *u = get_visible_unit(rc);
288  return unit_traits(u);
289 }
290 REPORT_GENERATOR(selected_unit_traits, rc)
291 {
292  const unit *u = get_selected_unit(rc);
293  return unit_traits(u);
294 }
295 
296 static config unit_status(reports::context & rc, const unit* u)
297 {
298  if (!u) return config();
299  config res;
300  map_location displayed_unit_hex = rc.screen().displayed_unit_hex();
301  if (rc.map().on_board(displayed_unit_hex) && u->invisible(displayed_unit_hex)) {
302  add_status(res, "misc/invisible.png", N_("invisible: "),
303  N_("This unit is invisible. It cannot be seen or attacked by enemy units."));
304  }
305  if (u->get_state(unit::STATE_SLOWED)) {
306  add_status(res, "misc/slowed.png", N_("slowed: "),
307  N_("This unit has been slowed. It will only deal half its normal damage when attacking and its movement cost is doubled."));
308  }
310  add_status(res, "misc/poisoned.png", N_("poisoned: "),
311  N_("This unit is poisoned. It will lose 8 HP every turn until it can seek a cure to the poison in a village or from a friendly unit with the ‘cures’ ability.\n\nUnits cannot be killed by poison alone. The poison will not reduce it below 1 HP."));
312  }
314  add_status(res, "misc/petrified.png", N_("petrified: "),
315  N_("This unit has been petrified. It may not move or attack."));
316  }
317  return res;
318 }
320 {
321  const unit *u = get_visible_unit(rc);
322  return unit_status(rc,u);
323 }
324 REPORT_GENERATOR(selected_unit_status, rc)
325 {
326  const unit *u = get_selected_unit(rc);
327  return unit_status(rc, u);
328 }
329 
331 {
332  if (!u) return config();
333  std::ostringstream str, tooltip;
334  const std::string align = unit_type::alignment_description(u->alignment(), u->gender());
335  const std::string align_id = u->alignment().to_string();
336  int cm = combat_modifier(rc.units(), rc.map(), rc.screen().displayed_unit_hex(), u->alignment(),
337  u->is_fearless());
338 
339  color_t color = font::weapon_color;
340  if (cm != 0)
341  color = (cm > 0) ? font::good_dmg_color : font::bad_dmg_color;
342 
343  str << align << " (" << span_color(color) << utils::signed_percent(cm)
344  << naps << ")";
345 
346  tooltip << _("Alignment: ") << "<b>" << align << "</b>\n"
347  << string_table[align_id + "_description"];
348 
349  return text_report(str.str(), tooltip.str(), "time_of_day");
350 }
352 {
353  const unit *u = get_visible_unit(rc);
354  return unit_alignment(rc, u);
355 }
356 REPORT_GENERATOR(selected_unit_alignment, rc)
357 {
358  const unit *u = get_selected_unit(rc);
359  return unit_alignment(rc, u);
360 }
361 
362 
363 static config unit_abilities(const unit* u)
364 {
365  if (!u) return config();
366  config res;
367 
368  boost::dynamic_bitset<> active;
369  const std::vector<std::tuple<std::string, t_string,t_string,t_string>> &abilities = u->ability_tooltips(&active);
370  const std::size_t abilities_size = abilities.size();
371  for ( std::size_t i = 0; i != abilities_size; ++i )
372  {
373  // Aliases for readability:
374  const std::string& id = std::get<0>(abilities[i]);
375  const std::string& base_name = std::get<1>(abilities[i]).base_str();
376  const t_string& display_name = std::get<2>(abilities[i]);
377  const t_string& description = std::get<3>(abilities[i]);
378 
379  std::ostringstream str, tooltip;
380 
381  if ( active[i] )
382  str << display_name;
383  else
384  str << span_color(font::inactive_ability_color) << display_name << naps;
385  if ( i + 1 != abilities_size )
386  str << ", ";
387 
388  tooltip << _("Ability: ") << "<b>" << display_name << "</b>";
389  if ( !active[i] )
390  tooltip << "<i>" << _(" (inactive)") << "</i>";
391  tooltip << '\n' << description;
392 
393  add_text(res, str.str(), tooltip.str(), "ability_" + id + base_name);
394  }
395  return res;
396 }
398 {
399  const unit *u = get_visible_unit(rc);
400  return unit_abilities(u);
401 }
402 REPORT_GENERATOR(selected_unit_abilities, rc)
403 {
404  const unit *u = get_selected_unit(rc);
405  return unit_abilities(u);
406 }
407 
408 
409 static config unit_hp(reports::context& rc, const unit* u)
410 {
411  if (!u) return config();
412  std::ostringstream str, tooltip;
413  str << span_color(u->hp_color()) << u->hitpoints()
414  << '/' << u->max_hitpoints() << naps;
415 
416  std::set<std::string> resistances_table;
417 
418  bool att_def_diff = false;
419  map_location displayed_unit_hex = rc.screen().displayed_unit_hex();
420  for (const utils::string_map::value_type &resist : u->get_base_resistances())
421  {
422  std::ostringstream line;
423  line << translation::gettext(resist.first.c_str()) << ": ";
424  // Some units have different resistances when attacking or defending.
425  int res_att = 100 - u->resistance_against(resist.first, true, displayed_unit_hex);
426  int res_def = 100 - u->resistance_against(resist.first, false, displayed_unit_hex);
427  const std::string def_color = unit_helper::resistance_color(res_def);
428  if (res_att == res_def) {
429  line << "<span foreground=\"" << def_color << "\">" << utils::signed_percent(res_def)
430  << naps << '\n';
431  } else {
432  const std::string att_color = unit_helper::resistance_color(res_att);
433  line << "<span foreground=\"" << att_color << "\">" << utils::signed_percent(res_att)
434  << naps << "/"
435  << "<span foreground=\"" << def_color << "\">" << utils::signed_percent(res_def)
436  << naps << '\n';
437  att_def_diff = true;
438  }
439  resistances_table.insert(line.str());
440  }
441 
442  tooltip << _("Resistances: ");
443  if (att_def_diff)
444  tooltip << _("(Att / Def)");
445  tooltip << '\n';
446  for (const std::string &line : resistances_table) {
447  tooltip << line;
448  }
449  return text_report(str.str(), tooltip.str());
450 }
452 {
453  const unit *u = get_visible_unit(rc);
454  return unit_hp(rc, u);
455 }
456 REPORT_GENERATOR(selected_unit_hp, rc)
457 {
458  const unit *u = get_selected_unit(rc);
459  return unit_hp(rc, u);
460 }
461 
462 static config unit_xp(const unit* u)
463 {
464  if (!u) return config();
465  std::ostringstream str, tooltip;
466  str << span_color(u->xp_color()) << u->experience()
467  << '/' << u->max_experience() << naps;
468 
470  tooltip << _("Experience Modifier: ") << exp_mod << '%';
471  return text_report(str.str(), tooltip.str());
472 }
474 {
475  const unit *u = get_visible_unit(rc);
476  return unit_xp(u);
477 }
478 REPORT_GENERATOR(selected_unit_xp, rc)
479 {
480  const unit *u = get_selected_unit(rc);
481  return unit_xp(u);
482 }
483 
485 {
486  if (!u) return config();
487  config res;
488  typedef std::pair<std::string, std::string> pair_string;
489  for (const pair_string &ps : u->advancement_icons()) {
490  add_image(res, ps.first, ps.second);
491  }
492  return res;
493 }
495 {
496  const unit *u = get_visible_unit(rc);
497  return unit_advancement_options(u);
498 }
499 REPORT_GENERATOR(selected_unit_advancement_options, rc)
500 {
501  const unit *u = get_selected_unit(rc);
502  return unit_advancement_options(u);
503 }
504 
505 static config unit_defense(reports::context & rc, const unit* u, const map_location& displayed_unit_hex)
506 {
507  if(!u) {
508  return config();
509  }
510 
511  std::ostringstream str, tooltip;
512  const gamemap &map = rc.map();
513  if(!rc.map().on_board(displayed_unit_hex)) {
514  return config();
515  }
516 
517  const t_translation::terrain_code &terrain = map[displayed_unit_hex];
518  int def = 100 - u->defense_modifier(terrain);
519  color_t color = game_config::red_to_green(def);
520  str << span_color(color) << def << '%' << naps;
521  tooltip << _("Terrain: ") << "<b>" << map.get_terrain_info(terrain).description() << "</b>\n";
522 
523  const t_translation::ter_list &underlyings = map.underlying_def_terrain(terrain);
524  if (underlyings.size() != 1 || underlyings.front() != terrain)
525  {
526  bool revert = false;
527  for (const t_translation::terrain_code &t : underlyings)
528  {
529  if (t == t_translation::MINUS) {
530  revert = true;
531  } else if (t == t_translation::PLUS) {
532  revert = false;
533  } else {
534  int t_def = 100 - u->defense_modifier(t);
535  color_t t_color = game_config::red_to_green(t_def);
536  tooltip << '\t' << map.get_terrain_info(t).description() << ": "
537  << span_color(t_color) << t_def << '%' << naps
538  << (revert ? _("maximum^max.") : _("minimum^min.")) << '\n';
539  }
540  }
541  }
542 
543  tooltip << "<b>" << _("Defense: ") << span_color(color) << def << '%' << naps << "</b>";
544  return text_report(str.str(), tooltip.str());
545 }
547 {
548  const unit *u = get_visible_unit(rc);
549  const map_location& displayed_unit_hex = rc.screen().displayed_unit_hex();
550  return unit_defense(rc, u, displayed_unit_hex);
551 }
552 REPORT_GENERATOR(selected_unit_defense, rc)
553 {
554  const unit *u = get_selected_unit(rc);
555  const map_location& selected_hex = rc.screen().selected_hex();
556  return unit_defense(rc, u, selected_hex);
557 }
558 
559 static config unit_vision(const unit* u)
560 {
561  if (!u) return config();
562 
563  // TODO
564  std::ostringstream str;
565  if (u->vision() != u->total_movement()) {
566  str << _("vision: ") << u->vision(); }
567  return text_report(str.str());
568 }
570 {
571  const unit* u = get_visible_unit(rc);
572  return unit_vision(u);
573 }
574 REPORT_GENERATOR(selected_unit_vision, rc)
575 {
576  const unit* u = get_selected_unit(rc);
577  return unit_vision(u);
578 }
579 
580 static config unit_moves(reports::context & rc, const unit* u)
581 {
582  if (!u) return config();
583  std::ostringstream str, tooltip;
584  double movement_frac = 1.0;
585 
586  std::set<terrain_movement> terrain_moves;
587 
588  if (u->side() == rc.screen().playing_side()) {
589  movement_frac = static_cast<double>(u->movement_left()) / std::max<int>(1, u->total_movement());
590  if (movement_frac > 1.0)
591  movement_frac = 1.0;
592  }
593 
594  tooltip << _("Movement Costs:") << "\n";
597  continue;
598 
599  const terrain_type& info = rc.map().get_terrain_info(terrain);
600 
601  if (info.union_type().size() == 1 && info.union_type()[0] == info.number() && info.is_nonnull()) {
602  terrain_moves.emplace(info.name(), u->movement_cost(terrain));
603  }
604  }
605 
606  for (const terrain_movement& tm : terrain_moves) {
607  tooltip << tm.name << ": ";
608 
609  std::string color;
610  //movement - range: 1 .. 5, movetype::UNREACHABLE=impassable
611  const bool cannot_move = tm.moves > u->total_movement();
612  if (cannot_move) // cannot move in this terrain
613  color = "red";
614  else if (tm.moves > 1)
615  color = "yellow";
616  else
617  color = "white";
618  tooltip << "<span foreground=\"" << color << "\">";
619  // A 5 MP margin; if the movement costs go above
620  // the unit's max moves + 5, we replace it with dashes.
621  if (cannot_move && (tm.moves > u->total_movement() + 5)) {
622  tooltip << font::unicode_figure_dash;
623  } else {
624  tooltip << tm.moves;
625  }
626  tooltip << naps << '\n';
627  }
628 
629  int grey = 128 + static_cast<int>((255 - 128) * movement_frac);
630  color_t c = color_t(grey, grey, grey);
631  str << span_color(c) << u->movement_left() << '/' << u->total_movement() << naps;
632  return text_report(str.str(), tooltip.str());
633 }
635 {
636  const unit *u = get_visible_unit(rc);
637  return unit_moves(rc, u);
638 }
639 REPORT_GENERATOR(selected_unit_moves, rc)
640 {
641  const unit *u = get_selected_unit(rc);
642  return unit_moves(rc, u);
643 }
644 
645 static inline const color_t attack_info_percent_color(int resistance)
646 {
647  // Compare unit_helper::resistance_color()
648  if (resistance < 0) return font::BAD_COLOR;
649  if (resistance > 0) return font::GOOD_COLOR;
650  return font::YELLOW_COLOR;
651 }
652 
653 static int attack_info(reports::context & rc, const attack_type &at, config &res, const unit &u, const map_location &displayed_unit_hex)
654 {
655  std::ostringstream str, tooltip;
656  int damage = 0;
657 
658  {
659  auto ctx = at.specials_context(unit_const_ptr(&u), displayed_unit_hex, u.side() == rc.screen().playing_side());
660  int base_damage = at.damage();
661  int specials_damage = at.modified_damage(false);
662  int damage_multiplier = 100;
663  const_attack_ptr weapon = at.shared_from_this();
664  int tod_bonus = combat_modifier(rc.units(), rc.map(), displayed_unit_hex, u.alignment(), u.is_fearless());
665  damage_multiplier += tod_bonus;
666  int leader_bonus = under_leadership(rc.units(), displayed_unit_hex, weapon).first;
667  if (leader_bonus != 0)
668  damage_multiplier += leader_bonus;
669 
671  int damage_divisor = slowed ? 20000 : 10000;
672  // Assume no specific resistance (i.e. multiply by 100).
673  damage = round_damage(specials_damage, damage_multiplier * 100, damage_divisor);
674 
675  // Hit points are used to calculate swarm, so they need to be bounded.
676  unsigned max_hp = u.max_hitpoints();
677  unsigned cur_hp = std::min<unsigned>(std::max(0, u.hitpoints()), max_hp);
678 
679  unsigned base_attacks = at.num_attacks();
680  unsigned min_attacks, max_attacks;
681  at.modified_attacks(false, min_attacks, max_attacks);
682  unsigned num_attacks = swarm_blows(min_attacks, max_attacks, cur_hp, max_hp);
683 
684  color_t dmg_color = font::weapon_color;
685  if ( damage > specials_damage )
686  dmg_color = font::good_dmg_color;
687  else if ( damage < specials_damage )
688  dmg_color = font::bad_dmg_color;
689 
690  str << span_color(dmg_color) << " " << damage << naps << span_color(font::weapon_color)
691  << font::weapon_numbers_sep << num_attacks << ' ' << at.name()
692  << "</span>\n";
693  tooltip << _("Weapon: ") << "<b>" << at.name() << "</b>\n"
694  << _("Damage: ") << "<b>" << damage << "</b>\n";
695 
696  if ( tod_bonus || leader_bonus || slowed || specials_damage != base_damage )
697  {
698  tooltip << '\t' << _("Base damage: ") << base_damage << '\n';
699  if ( specials_damage != base_damage ) {
700  tooltip << '\t' << _("With specials: ") << specials_damage << '\n';
701  }
702  if (tod_bonus) {
703  tooltip << '\t' << _("Time of day: ")
704  << utils::signed_percent(tod_bonus) << '\n';
705  }
706  if (leader_bonus) {
707  tooltip << '\t' << _("Leadership: ")
708  << utils::signed_percent(leader_bonus) << '\n';
709  }
710  if (slowed) {
711  tooltip << '\t' << _("Slowed: ") << "/ 2" << '\n';
712  }
713  }
714 
715  tooltip << _("Attacks: ") << "<b>" << num_attacks << "</b>\n";
716  if ( max_attacks != min_attacks && cur_hp != max_hp ) {
717  if ( max_attacks < min_attacks ) {
718  // "Reverse swarm"
719  tooltip << '\t' << _("Max swarm bonus: ") << (min_attacks-max_attacks) << '\n';
720  tooltip << '\t' << _("Swarm: ") << "* "<< (100 - cur_hp*100/max_hp) << "%\n";
721  tooltip << '\t' << _("Base attacks: ") << '+' << base_attacks << '\n';
722  // The specials line will not necessarily match up with how the
723  // specials are calculated, but for an unusual case, simple brevity
724  // trumps complexities.
725  if ( max_attacks != base_attacks ) {
726  int attack_diff = static_cast<int>(max_attacks) - static_cast<int>(base_attacks);
727  tooltip << '\t' << _("Specials: ") << utils::signed_value(attack_diff) << '\n';
728  }
729  }
730  else {
731  // Regular swarm
732  tooltip << '\t' << _("Base attacks: ") << base_attacks << '\n';
733  if ( max_attacks != base_attacks ) {
734  tooltip << '\t' << _("With specials: ") << max_attacks << '\n';
735  }
736  if ( min_attacks != 0 ) {
737  tooltip << '\t' << _("Subject to swarm: ") << (max_attacks-min_attacks) << '\n';
738  }
739  tooltip << '\t' << _("Swarm: ") << "* "<< (cur_hp*100/max_hp) << "%\n";
740  }
741  }
742  else if ( num_attacks != base_attacks ) {
743  tooltip << '\t' << _("Base attacks: ") << base_attacks << '\n';
744  tooltip << '\t' << _("With specials: ") << num_attacks << '\n';
745  }
746 
747  add_text(res, flush(str), flush(tooltip));
748 
749  std::string range = string_table["range_" + at.range()];
750  std::string lang_type = string_table["type_" + at.type()];
751 
752  str << span_color(font::weapon_details_color) << " " << " "
753  << range << font::weapon_details_sep
754  << lang_type << "</span>\n";
755 
756  tooltip << _("Weapon range: ") << "<b>" << range << "</b>\n"
757  << _("Damage type: ") << "<b>" << lang_type << "</b>\n"
758  << _("Damage versus: ") << '\n';
759 
760  // Show this weapon damage and resistance against all the different units.
761  // We want weak resistances (= good damage) first.
762  std::map<int, std::set<std::string>, std::greater<int>> resistances;
763  std::set<std::string> seen_types;
764  const team &unit_team = rc.dc().get_team(u.side());
765  const team &viewing_team = rc.teams()[rc.screen().viewing_team()];
766  for (const unit &enemy : rc.units())
767  {
768  if (enemy.incapacitated()) //we can't attack statues so don't display them in this tooltip
769  continue;
770  if (!unit_team.is_enemy(enemy.side()))
771  continue;
772  const map_location &loc = enemy.get_location();
773  if (viewing_team.fogged(loc) ||
774  (viewing_team.is_enemy(enemy.side()) && enemy.invisible(loc)))
775  continue;
776  bool new_type = seen_types.insert(enemy.type_id()).second;
777  if (new_type) {
778  int resistance = enemy.resistance_against(at, false, loc);
779  resistances[resistance].insert(enemy.type_name());
780  }
781  }
782 
783  typedef std::pair<int, std::set<std::string>> resist_units;
784  for (const resist_units &resist : resistances) {
785  int damage_with_resistance = round_damage(specials_damage, damage_multiplier * resist.first, damage_divisor);
786  tooltip << "<b>" << damage_with_resistance << "</b> "
787  << "<span color='" << attack_info_percent_color(resist.first-100).to_hex_string() << "'>"
788  << "<i>(" << utils::signed_percent(resist.first-100) << ")</i>"
789  << naps
790  << " : \t" // spaces to align the tab to a multiple of 8
791  << utils::join(resist.second, " " + font::unicode_bullet + " ") << '\n';
792  }
793  add_text(res, flush(str), flush(tooltip));
794 
795  const std::string &accuracy_parry = at.accuracy_parry_description();
796  if (!accuracy_parry.empty())
797  {
799  << " " << accuracy_parry << "</span>\n";
800  int accuracy = at.accuracy();
801  if (accuracy) {
802  tooltip << _("Accuracy:") << "<b>"
803  << utils::signed_percent(accuracy) << "</b>\n";
804  }
805  int parry = at.parry();
806  if (parry) {
807  tooltip << _("Parry:") << "<b>"
808  << utils::signed_percent(parry) << "</b>\n";
809  }
810  add_text(res, flush(str), flush(tooltip));
811  }
812  }
813 
814  {
815  auto ctx = at.specials_context_for_listing(u.side() == rc.screen().playing_side());
816  boost::dynamic_bitset<> active;
817  const std::vector<std::pair<t_string, t_string>> &specials = at.special_tooltips(&active);
818  const std::size_t specials_size = specials.size();
819  for ( std::size_t i = 0; i != specials_size; ++i )
820  {
821  // Aliases for readability:
822  const t_string &name = specials[i].first;
823  const t_string &description = specials[i].second;
824  const color_t &details_color = active[i] ? font::weapon_details_color :
826 
827  str << span_color(details_color) << " " << " " << name << naps << '\n';
828  std::string help_page = "weaponspecial_" + name.base_str();
829  tooltip << _("Weapon special: ") << "<b>" << name << "</b>";
830  if ( !active[i] )
831  tooltip << "<i>" << _(" (inactive)") << "</i>";
832  tooltip << '\n' << description;
833 
834  add_text(res, flush(str), flush(tooltip), help_page);
835  }
836  }
837  return damage;
838 }
839 
840 // Conversion routine for both unscathed and damage change percentage.
841 static std::string format_prob(double prob)
842 {
843  if(prob > 0.9995) {
844  return "100%";
845  } else if(prob < 0.0005) {
846  return "0%";
847  }
848  std::ostringstream res;
849  res << std::setprecision(prob < 0.1 ? 2 : 3) << 100.0 * prob << "%";
850  return res.str();
851 }
852 
853 static std::string format_hp(unsigned hp)
854 {
855  std::ostringstream res;
856  res << ' ' << std::setw(3) << hp;
857  return res.str();
858 }
859 
860 static config unit_weapons(reports::context & rc, const unit *attacker, const map_location &attacker_pos, const unit *defender, bool show_attacker)
861 {
862  if (!attacker || !defender) return config();
863 
864  const unit* u = show_attacker ? attacker : defender;
865  const map_location unit_loc = show_attacker ? attacker_pos : defender->get_location();
866 
867  std::ostringstream str, tooltip;
868  config res;
869 
870  std::vector<battle_context> weapons;
871  for (unsigned int i = 0; i < attacker->attacks().size(); i++) {
872  // skip weapons with attack_weight=0
873  if (attacker->attacks()[i].attack_weight() > 0) {
874  weapons.emplace_back(rc.units(), attacker_pos, defender->get_location(), i, -1, 0.0, nullptr, attacker);
875  }
876  }
877 
878  for (const battle_context& weapon : weapons) {
879 
880  // Predict the battle outcome.
881  combatant attacker_combatant(weapon.get_attacker_stats());
882  combatant defender_combatant(weapon.get_defender_stats());
883  attacker_combatant.fight(defender_combatant);
884 
885  const battle_context_unit_stats& context_unit_stats =
886  show_attacker ? weapon.get_attacker_stats() : weapon.get_defender_stats();
887 
888  int total_damage = 0;
889  int base_damage = 0;
890  int num_blows = 0;
891  int chance_to_hit = 0;
892  t_string weapon_name = _("weapon^None");
893 
894  color_t dmg_color = font::weapon_color;
895  if (context_unit_stats.weapon) {
896  base_damage = attack_info(rc, *context_unit_stats.weapon, res, *u, unit_loc);
897  total_damage = context_unit_stats.damage;
898  num_blows = context_unit_stats.num_blows;
899  chance_to_hit = context_unit_stats.chance_to_hit;
900  weapon_name = context_unit_stats.weapon->name();
901 
902  if ( total_damage > base_damage )
903  dmg_color = font::good_dmg_color;
904  else if ( total_damage < base_damage )
905  dmg_color = font::bad_dmg_color;
906  } else {
907  str << span_color(font::weapon_color) << weapon_name << naps << "\n";
908  tooltip << _("Weapon: ") << "<b>" << weapon_name << "</b>\n"
909  << _("Damage: ") << "<b>" << "0" << "</b>\n";
910  }
911 
912  color_t chance_color = game_config::red_to_green(chance_to_hit);
913 
914  // Total damage.
915  str << " " << span_color(dmg_color) << total_damage << naps << span_color(font::weapon_color)
916  << font::unicode_en_dash << num_blows
917  << " (" << span_color(chance_color) << chance_to_hit << "%" << naps << ")"
918  << naps << "\n";
919 
920  tooltip << _("Weapon: ") << "<b>" << weapon_name << "</b>\n"
921  << _("Total damage") << "<b>" << total_damage << "</b>\n";
922 
923  // Create the hitpoints distribution.
924  std::vector<std::pair<int, double>> hp_prob_vector;
925 
926  // First, we sort the probabilities in ascending order.
927  std::vector<std::pair<double, int>> prob_hp_vector;
928  int i;
929 
930  combatant* c = show_attacker ? &attacker_combatant : &defender_combatant;
931 
932  for(i = 0; i < static_cast<int>(c->hp_dist.size()); i++) {
933  double prob = c->hp_dist[i];
934 
935  // We keep only values above 0.1%.
936  if(prob > 0.001)
937  prob_hp_vector.emplace_back(prob, i);
938  }
939 
940  std::sort(prob_hp_vector.begin(), prob_hp_vector.end());
941 
942  //TODO fendrin -- make that dynamically
943  int max_hp_distrib_rows_ = 10;
944 
945  // We store a few of the highest probability hitpoint values.
946  int nb_elem = std::min<int>(max_hp_distrib_rows_, prob_hp_vector.size());
947 
948  for(i = prob_hp_vector.size() - nb_elem;
949  i < static_cast<int>(prob_hp_vector.size()); i++) {
950 
951  hp_prob_vector.emplace_back(prob_hp_vector[i].second, prob_hp_vector[i].first);
952  }
953 
954  // Then, we sort the hitpoint values in ascending order.
955  std::sort(hp_prob_vector.begin(), hp_prob_vector.end());
956  // And reverse the order. Might be doable in a better manor.
957  std::reverse(hp_prob_vector.begin(), hp_prob_vector.end());
958 
959  for(i = 0; i < static_cast<int>(hp_prob_vector.size()); i++) {
960 
961  int hp = hp_prob_vector[i].first;
962  double prob = hp_prob_vector[i].second;
963  color_t prob_color = game_config::blue_to_white(prob * 100.0, true);
964 
965  str << span_color(font::weapon_details_color) << " " << " "
966  << span_color(u->hp_color(hp)) << format_hp(hp) << naps
967  << " " << font::weapon_numbers_sep << " "
968  << span_color(prob_color) << format_prob(prob) << naps
969  << naps << "\n";
970  }
971 
972  add_text(res, flush(str), flush(tooltip));
973  }
974  return res;
975 }
976 
977 static config unit_weapons(reports::context & rc, const unit *u)
978 {
979  config res = config();
980  if ((u != nullptr) && (!u->attacks().empty())) {
981  map_location displayed_unit_hex = rc.screen().displayed_unit_hex();
982 
983  const std::string attack_headline = _n("Attack", "Attacks", u->attacks().size());
984 
986  + attack_headline + "</span>" + '\n', "");
987 
988  for (const attack_type &at : u->attacks())
989  {
990  attack_info(rc, at, res, *u, displayed_unit_hex);
991  }
992  }
993  return res;
994 }
996 {
997  const unit *u = get_visible_unit(rc);
998  if (!u) return config();
999 
1000  return unit_weapons(rc, u);
1001 }
1002 REPORT_GENERATOR(highlighted_unit_weapons, rc)
1003 {
1004  const unit *u = get_selected_unit(rc);
1005  const unit *sec_u = get_visible_unit(rc);
1006 
1007  if (!u) return config();
1008  if (!sec_u || u == sec_u) return unit_weapons(rc, sec_u);
1009 
1010  map_location highlighted_hex = rc.screen().displayed_unit_hex();
1011  map_location attack_loc;
1012  if (rc.mhb())
1013  attack_loc = rc.mhb()->current_unit_attacks_from(highlighted_hex);
1014 
1015  if (!attack_loc.valid())
1016  return unit_weapons(rc, sec_u);
1017 
1018  return unit_weapons(rc, u, attack_loc, sec_u, false);
1019 }
1020 REPORT_GENERATOR(selected_unit_weapons, rc)
1021 {
1022  const unit *u = get_selected_unit(rc);
1023  const unit *sec_u = get_visible_unit(rc);
1024 
1025  if (!u) return config();
1026  if (!sec_u || u == sec_u) return unit_weapons(rc, u);
1027 
1028  map_location highlighted_hex = rc.screen().displayed_unit_hex();
1029  map_location attack_loc;
1030  if (rc.mhb())
1031  attack_loc = rc.mhb()->current_unit_attacks_from(highlighted_hex);
1032 
1033  if (!attack_loc.valid())
1034  return unit_weapons(rc, u);
1035 
1036  return unit_weapons(rc, u, attack_loc, sec_u, true);
1037 }
1038 
1039 REPORT_GENERATOR(unit_image,rc)
1040 {
1041  const unit *u = get_visible_unit(rc);
1042  if (!u) return config();
1043  return image_report(u->absolute_image() + u->image_mods());
1044 }
1045 REPORT_GENERATOR(selected_unit_image, rc)
1046 {
1047  const unit *u = get_selected_unit(rc);
1048  if (!u) return config();
1049  return image_report(u->absolute_image() + u->image_mods());
1050 }
1051 
1052 REPORT_GENERATOR(selected_unit_profile, rc)
1053 {
1054  const unit *u = get_selected_unit(rc);
1055  if (!u) return config();
1056  return image_report(u->small_profile());
1057 }
1058 REPORT_GENERATOR(unit_profile, rc)
1059 {
1060  const unit *u = get_visible_unit(rc);
1061  if (!u) return config();
1062  return image_report(u->small_profile());
1063 }
1064 
1065 REPORT_GENERATOR(tod_stats, rc)
1066 {
1067  std::ostringstream tooltip;
1068  std::ostringstream text;
1069 
1070  const map_location& selected_hex = rc.screen().selected_hex();
1071  const map_location& mouseover_hex = rc.screen().mouseover_hex();
1072 
1073  const map_location& hex = mouseover_hex.valid() ? mouseover_hex : selected_hex;
1074 
1075  const std::vector<time_of_day>& schedule = rc.tod().times(hex);
1076 
1077  int current = rc.tod().get_current_time(hex);
1078  int i = 0;
1079  for (const time_of_day& tod : schedule) {
1080  if (i == current) tooltip << "<big><b>";
1081  tooltip << tod.name << "\n";
1082  if (i == current) tooltip << "</b></big>";
1083  i++;
1084  }
1085 
1086  int times = schedule.size();
1087  text << current + 1 << "/" << times;
1088 
1089  return text_report(text.str(), tooltip.str(), "..schedule");
1090 }
1091 
1092 static config time_of_day_at(reports::context & rc, const map_location& mouseover_hex)
1093 {
1094  std::ostringstream tooltip;
1095  time_of_day tod;
1096  const team &viewing_team = rc.teams()[rc.screen().viewing_team()];
1097  if (viewing_team.shrouded(mouseover_hex)) {
1098  // Don't show time on shrouded tiles.
1099  tod = rc.tod().get_time_of_day();
1100  } else if (viewing_team.fogged(mouseover_hex)) {
1101  // Don't show illuminated time on fogged tiles.
1102  tod = rc.tod().get_time_of_day(mouseover_hex);
1103  } else {
1104  tod = rc.tod().get_illuminated_time_of_day(rc.units(), rc.map(), mouseover_hex);
1105  }
1106 
1107  int b = tod.lawful_bonus;
1108 
1109  std::string lawful_color("white");
1110  std::string chaotic_color("white");
1111  std::string liminal_color("white");
1112 
1113  if (b != 0) {
1114  lawful_color = (b > 0) ? "green" : "red";
1115  chaotic_color = (b < 0) ? "green" : "red";
1116  liminal_color = "red";
1117  }
1118  tooltip << tod.name << '\n'
1119  << _("Lawful units: ") << "<span foreground=\"" << lawful_color << "\">"
1120  << utils::signed_percent(b) << "</span>\n"
1121  << _("Neutral units: ") << utils::signed_percent(0) << '\n'
1122  << _("Chaotic units: ") << "<span foreground=\"" << chaotic_color << "\">"
1123  << utils::signed_percent(-b) << "</span>\n"
1124  << _("Liminal units: ") << "<span foreground=\"" << liminal_color << "\">"
1125  << utils::signed_percent(-(std::abs(b))) << "</span>\n";
1126 
1127  std::string tod_image = tod.image;
1128  if(tod.bonus_modified > 0) {
1129  tod_image += (formatter() << "~BLIT(" << game_config::images::tod_bright << ")").str();
1130  } else if(tod.bonus_modified < 0) {
1131  tod_image += (formatter() << "~BLIT(" << game_config::images::tod_dark << ")").str();
1132  }
1133 
1134  return image_report(tod_image, tooltip.str(), "time_of_day_" + tod.id);
1135 }
1137 {
1138  map_location mouseover_hex = rc.screen().mouseover_hex();
1139  if (mouseover_hex.valid()) return time_of_day_at(rc, mouseover_hex);
1140  return time_of_day_at(rc, rc.screen().selected_hex());
1141 }
1142 
1143 static config unit_box_at(reports::context & rc, const map_location& mouseover_hex)
1144 {
1145  std::ostringstream tooltip;
1146  time_of_day local_tod;
1147  time_of_day global_tod = rc.tod().get_time_of_day();
1148  const team &viewing_team = rc.teams()[rc.screen().viewing_team()];
1149  if (viewing_team.shrouded(mouseover_hex)) {
1150  // Don't show time on shrouded tiles.
1151  local_tod = global_tod;
1152  } else if (viewing_team.fogged(mouseover_hex)) {
1153  // Don't show illuminated time on fogged tiles.
1154  local_tod = rc.tod().get_time_of_day(mouseover_hex);
1155  } else {
1156  local_tod = rc.tod().get_illuminated_time_of_day(rc.units(), rc.map(),mouseover_hex);
1157  }
1158 
1159  int bonus = local_tod.lawful_bonus;
1160 
1161  std::string lawful_color("white");
1162  std::string chaotic_color("white");
1163  std::string liminal_color("white");
1164 
1165  if (bonus != 0) {
1166  lawful_color = (bonus > 0) ? "green" : "red";
1167  chaotic_color = (bonus < 0) ? "green" : "red";
1168  liminal_color = "red";
1169  }
1170  tooltip << local_tod.name << '\n'
1171  << _("Lawful units: ") << "<span foreground=\"" << lawful_color << "\">"
1172  << utils::signed_percent(bonus) << "</span>\n"
1173  << _("Neutral units: ") << utils::signed_percent(0) << '\n'
1174  << _("Chaotic units: ") << "<span foreground=\"" << chaotic_color << "\">"
1175  << utils::signed_percent(-bonus) << "</span>\n"
1176  << _("Liminal units: ") << "<span foreground=\"" << liminal_color << "\">"
1177  << utils::signed_percent(-(std::abs(bonus))) << "</span>\n";
1178 
1179  std::string local_tod_image = "themes/classic/" + local_tod.image;
1180  std::string global_tod_image = "themes/classic/" + global_tod.image;
1181  if(local_tod.bonus_modified != 0) {
1182  local_tod_image += "~BLIT(";
1183  if (local_tod.bonus_modified > 0) local_tod_image += game_config::images::tod_bright;
1184  else if (local_tod.bonus_modified < 0) local_tod_image += game_config::images::tod_dark;
1185  local_tod_image += ")";
1186  }
1187 
1188  const gamemap &map = rc.map();
1189  t_translation::terrain_code terrain = map.get_terrain(mouseover_hex);
1190 
1191  //if (t_translation::terrain_matches(terrain, t_translation::ALL_OFF_MAP))
1192  // return config();
1193 
1194  //if (map.is_keep(mouseover_hex)) {
1195  // add_image(cfg, "icons/terrain/terrain_type_keep.png", "");
1196  //}
1197 
1198  const t_translation::ter_list& underlying_terrains = map.underlying_union_terrain(terrain);
1199 
1200  std::string bg_terrain_image;
1201 
1202  for (const t_translation::terrain_code& underlying_terrain : underlying_terrains) {
1203  const std::string& terrain_id = map.get_terrain_info(underlying_terrain).id();
1204  bg_terrain_image = "~BLIT(unit_env/terrain/terrain-" + terrain_id + ".png)" + bg_terrain_image;
1205  }
1206 
1207  std::stringstream color;
1208  color << local_tod.color;
1209 
1210  bg_terrain_image = bg_terrain_image + "~CS(" + color.str() + ")";
1211 
1212  const unit *u = get_visible_unit(rc);
1213  std::string unit_image;
1214  if (u)
1215  unit_image = "~BLIT(" + u->absolute_image() + u->image_mods() + ",35,22)";
1216 
1217  std::string tod_image = global_tod_image + "~BLIT(" + local_tod_image + ")";
1218 
1219  return image_report(tod_image + bg_terrain_image + unit_image, tooltip.str(), "time_of_day");
1220 }
1221 REPORT_GENERATOR(unit_box, rc)
1222 {
1223  map_location mouseover_hex = rc.screen().mouseover_hex();
1224  return unit_box_at(rc, mouseover_hex);
1225 }
1226 
1227 
1229 {
1230  std::ostringstream str, tooltip;
1231  str << rc.tod().turn();
1232  int nb = rc.tod().number_of_turns();
1233  if (nb != -1) str << '/' << nb;
1234 
1235  tooltip << _("Turn Number");
1236  if(nb != -1) {
1237  tooltip << "\n\n" << _("When the game reaches the number of turns indicated by the second number, it will end.");
1238  }
1239  return text_report(str.str(), tooltip.str());
1240 }
1241 
1243 {
1244  std::ostringstream str;
1245  int viewing_side = rc.screen().viewing_side();
1246  // Suppose the full unit map is applied.
1247  int fake_gold = rc.dc().get_team(viewing_side).gold();
1248 
1249  if (rc.wb())
1250  fake_gold -= rc.wb()->get_spent_gold_for(viewing_side);
1251  char const *end = naps;
1252  if (viewing_side != rc.screen().playing_side()) {
1253  str << span_color(font::GRAY_COLOR);
1254  }
1255  else if (fake_gold < 0) {
1256  str << span_color(font::BAD_COLOR);
1257  }
1258  else {
1259  end = "";
1260  }
1261  str << utils::half_signed_value(fake_gold) << end;
1262  return text_report(str.str(), _("Gold") + "\n\n" + _("The amount of gold currently available to recruit and maintain your army."));
1263 }
1264 
1265 REPORT_GENERATOR(villages, rc)
1266 {
1267  std::ostringstream str;
1268  int viewing_side = rc.screen().viewing_side();
1269  const team &viewing_team = rc.dc().get_team(viewing_side);
1270  str << viewing_team.villages().size() << '/';
1271  if (viewing_team.uses_shroud()) {
1272  int unshrouded_villages = 0;
1273  for (const map_location &loc : rc.map().villages()) {
1274  if (!viewing_team.shrouded(loc))
1275  ++unshrouded_villages;
1276  }
1277  str << unshrouded_villages;
1278  } else {
1279  str << rc.map().villages().size();
1280  }
1281  return gray_inactive(rc,str.str(), _("Villages") + "\n\n" + _("The fraction of known villages that your side has captured."));
1282 }
1283 
1284 REPORT_GENERATOR(num_units, rc)
1285 {
1286  return gray_inactive(rc, std::to_string(rc.dc().side_units(rc.screen().viewing_side())), _("Units") + "\n\n" + _("The total number of units on your side."));
1287 }
1288 
1289 REPORT_GENERATOR(upkeep, rc)
1290 {
1291  std::ostringstream str;
1292  int viewing_side = rc.screen().viewing_side();
1293  const team &viewing_team = rc.dc().get_team(viewing_side);
1294  team_data td = rc.dc().calculate_team_data(viewing_team);
1295  str << td.expenses << " (" << td.upkeep << ")";
1296  return gray_inactive(rc,str.str(), _("Upkeep") + "\n\n" + _("The expenses incurred at the end of every turn to maintain your army. The first number is the amount of gold that will be deducted. The second is the total cost of upkeep, including that covered by villages — in other words, the amount of gold that would be deducted if you lost all villages."));
1297 }
1298 
1299 REPORT_GENERATOR(expenses, rc)
1300 {
1301  int viewing_side = rc.screen().viewing_side();
1302  const team &viewing_team = rc.dc().get_team(viewing_side);
1303  team_data td = rc.dc().calculate_team_data(viewing_team);
1304  return gray_inactive(rc,std::to_string(td.expenses));
1305 }
1306 
1307 REPORT_GENERATOR(income, rc)
1308 {
1309  std::ostringstream str;
1310  int viewing_side = rc.screen().viewing_side();
1311  const team &viewing_team = rc.dc().get_team(viewing_side);
1312  team_data td = rc.dc().calculate_team_data(viewing_team);
1313  char const *end = naps;
1314  if (viewing_side != rc.screen().playing_side()) {
1315  if (td.net_income < 0) {
1316  td.net_income = - td.net_income;
1317  str << span_color(font::GRAY_COLOR);
1318  str << font::unicode_minus;
1319  }
1320  else {
1321  str << span_color(font::GRAY_COLOR);
1322  }
1323  }
1324  else if (td.net_income < 0) {
1325  td.net_income = - td.net_income;
1326  str << span_color(font::BAD_COLOR);
1327  str << font::unicode_minus;
1328  }
1329  else {
1330  end = "";
1331  }
1332  str << td.net_income << end;
1333  return text_report(str.str(), _("Income") + "\n\n" + _("The amount of gold you gain each turn from your controlled villages, or the amount of gold you will lose each turn for unit upkeep."));
1334 }
1335 
1336 namespace {
1337 void blit_tced_icon(config &cfg, const std::string &terrain_id, const std::string &icon_image, bool high_res,
1338  const std::string &terrain_name) {
1339  const std::string tc_base = high_res ? "images/buttons/icon-base-32.png" : "images/buttons/icon-base-16.png";
1340  const std::string terrain_image = "terrain/" + icon_image + (high_res ? "_30.png" : ".png");
1341  add_image(cfg, tc_base + "~RC(magenta>" + terrain_id + ")~BLIT(" + terrain_image + ")", terrain_name);
1342 }
1343 }
1344 
1345 REPORT_GENERATOR(terrain_info, rc)
1346 {
1347  const gamemap &map = rc.map();
1348  map_location mouseover_hex = rc.screen().mouseover_hex();
1349 
1350  if (!map.on_board(mouseover_hex))
1351  mouseover_hex = rc.screen().selected_hex();
1352 
1353  if (!map.on_board(mouseover_hex))
1354  return config();
1355 
1356  t_translation::terrain_code terrain = map.get_terrain(mouseover_hex);
1358  return config();
1359 
1360  config cfg;
1361 
1362  bool high_res = false;
1363 
1364  if (display::get_singleton()->shrouded(mouseover_hex)) {
1365  return cfg;
1366  }
1367  //TODO
1368 // if (display::get_singleton()->fogged(mouseover_hex)) {
1369 // blit_tced_icon(cfg, "fog", high_res);
1370 // }
1371 //
1372 // if (map.is_keep(mouseover_hex)) {
1373 // blit_tced_icon(cfg, "keep", high_res);
1374 // }
1375 
1376  const t_translation::ter_list& underlying_terrains = map.underlying_union_terrain(terrain);
1377  for (const t_translation::terrain_code& underlying_terrain : underlying_terrains) {
1378 
1380  continue;
1381  const std::string& terrain_id = map.get_terrain_info(underlying_terrain).id();
1382  const std::string& terrain_name = map.get_terrain_string(underlying_terrain);
1383  const std::string& terrain_icon = map.get_terrain_info(underlying_terrain).icon_image();
1384  if (terrain_icon.empty())
1385  continue;
1386  blit_tced_icon(cfg, terrain_id, terrain_icon, high_res, terrain_name);
1387  }
1388  return cfg;
1389 }
1390 
1391 REPORT_GENERATOR(terrain, rc)
1392 {
1393  const gamemap &map = rc.map();
1394  int viewing_side = rc.screen().viewing_side();
1395  const team &viewing_team = rc.dc().get_team(viewing_side);
1396  map_location mouseover_hex = rc.screen().mouseover_hex();
1397  if (!map.on_board(mouseover_hex) || viewing_team.shrouded(mouseover_hex))
1398  return config();
1399 
1400  t_translation::terrain_code terrain = map.get_terrain(mouseover_hex);
1402  return config();
1403 
1404  std::ostringstream str;
1405  if (map.is_village(mouseover_hex))
1406  {
1407  int owner = rc.dc().village_owner(mouseover_hex) + 1;
1408  if (owner == 0 || viewing_team.fogged(mouseover_hex)) {
1409  str << map.get_terrain_info(terrain).income_description();
1410  } else if (owner == viewing_side) {
1411  str << map.get_terrain_info(terrain).income_description_own();
1412  } else if (viewing_team.is_enemy(owner)) {
1413  str << map.get_terrain_info(terrain).income_description_enemy();
1414  } else {
1415  str << map.get_terrain_info(terrain).income_description_ally();
1416  }
1417 
1418  const std::string& underlying_desc = map.get_underlying_terrain_string(terrain);
1419  if(!underlying_desc.empty()) {
1420  str << underlying_desc;
1421  }
1422  } else {
1423  str << map.get_terrain_string(terrain);
1424  }
1425 
1426  return text_report(str.str());
1427 }
1428 
1429 REPORT_GENERATOR(zoom_level, rc)
1430 {
1431  std::ostringstream text;
1432  std::ostringstream tooltip;
1433  std::ostringstream help;
1434 
1435  text << static_cast<int>(rc.screen().get_zoom_factor() * 100) << "%";
1436 
1437  return text_report(text.str(), tooltip.str(), help.str());
1438 }
1439 
1440 REPORT_GENERATOR(position, rc)
1441 {
1442  const gamemap &map = rc.map();
1443  map_location mouseover_hex = rc.screen().mouseover_hex(),
1444  displayed_unit_hex = rc.screen().displayed_unit_hex(),
1445  selected_hex = rc.screen().selected_hex();
1446 
1447  if (!map.on_board(mouseover_hex)) {
1448  if (!map.on_board(selected_hex))
1449  return config();
1450  else {
1451  mouseover_hex = selected_hex;
1452  }
1453  }
1454 
1455  t_translation::terrain_code terrain = map[mouseover_hex];
1457  return config();
1458 
1459  std::ostringstream str;
1460  str << mouseover_hex;
1461 
1462  const unit *u = get_visible_unit(rc);
1463  const team &viewing_team = rc.teams()[rc.screen().viewing_team()];
1464  if (!u ||
1465  (displayed_unit_hex != mouseover_hex &&
1466  displayed_unit_hex != rc.screen().selected_hex()) ||
1467  viewing_team.shrouded(mouseover_hex))
1468  return text_report(str.str());
1469 
1470  int move_cost = u->movement_cost(terrain);
1471  int defense = 100 - u->defense_modifier(terrain);
1472  if (move_cost < movetype::UNREACHABLE) {
1473  str << " " << defense << "%," << move_cost;
1474  } else if (mouseover_hex == displayed_unit_hex) {
1475  str << " " << defense << "%,‒";
1476  } else {
1477  str << " ‒";
1478  }
1479  return text_report(str.str());
1480 }
1481 
1482 REPORT_GENERATOR(side_playing, rc)
1483 {
1484  const team &active_team = rc.teams()[rc.screen().playing_team()];
1485  std::string flag_icon = active_team.flag_icon();
1486  std::string old_rgb = game_config::flag_rgb;
1487  std::string new_rgb = team::get_side_color_id(rc.screen().playing_side());
1488  std::string mods = "~RC(" + old_rgb + ">" + new_rgb + ")";
1489  if (flag_icon.empty())
1490  flag_icon = game_config::images::flag_icon;
1491  return image_report(flag_icon + mods, active_team.side_name());
1492 }
1493 
1494 REPORT_GENERATOR(observers, rc)
1495 {
1496  const std::set<std::string> &observers = rc.screen().observers();
1497  if (observers.empty())
1498  return config();
1499 
1500  std::ostringstream str;
1501  str << _("Observers:") << '\n';
1502  for (const std::string &obs : observers) {
1503  str << obs << '\n';
1504  }
1505  return image_report(game_config::images::observer, str.str());
1506 }
1507 
1508 /* TODO unused
1509 REPORT_GENERATOR(selected_terrain)
1510 {
1511  const std::string selected_terrain = editor::get_selected_terrain();
1512  if (selected_terrain.empty())
1513  return config();
1514  else
1515  return text_report(selected_terrain);
1516 }
1517 */
1518 
1519 /* TODO this is unused
1520 REPORT_GENERATOR(edit_left_button_function)
1521 {
1522  const std::string left_button_function = editor::get_left_button_function();
1523  if (left_button_function.empty())
1524  return config();
1525  else
1526  return text_report(left_button_function);
1527 }
1528 */
1529 
1530 REPORT_GENERATOR(report_clock, /*rc*/)
1531 {
1532  std::ostringstream ss;
1533 
1535  ? "%I:%M %p"
1536  : "%H:%M";
1537 
1538  std::time_t t = std::time(nullptr);
1539  ss << std::put_time(std::localtime(&t), format);
1540 
1541  return text_report(ss.str(), _("Clock"));
1542 }
1543 
1544 REPORT_GENERATOR(report_countdown, rc)
1545 {
1546  int viewing_side = rc.screen().viewing_side();
1547  const team &viewing_team = rc.dc().get_team(viewing_side);
1548  int min, sec;
1549  if (viewing_team.countdown_time() == 0)
1550  return report_report_clock(rc);
1551  std::ostringstream str;
1552  sec = viewing_team.countdown_time() / 1000;
1553  char const *end = naps;
1554  if (viewing_side != rc.screen().playing_side())
1555  str << span_color(font::GRAY_COLOR);
1556  else if (sec < 60)
1557  str << "<span foreground=\"#c80000\">";
1558  else if (sec < 120)
1559  str << "<span foreground=\"#c8c800\">";
1560  else
1561  end = "";
1562  min = sec / 60;
1563  str << min << ':';
1564  sec = sec % 60;
1565  if (sec < 10) str << '0';
1566  str << sec << end;
1567  return text_report(str.str(), _("Turn Countdown") + "\n\n" + _("Countdown until your turn automatically ends."));
1568 }
1569 
1571 {
1572  dynamic_generators_[name].reset(g);
1573 }
1574 
1575 config reports::generate_report(const std::string &name, reports::context & rc, bool only_static)
1576 {
1577  if (!only_static) {
1578  dynamic_report_generators::const_iterator i = dynamic_generators_.find(name);
1579  if (i != dynamic_generators_.end())
1580  return i->second->generate(rc);
1581  }
1582  static_report_generators::const_iterator j = static_generators.find(name);
1583  if (j != static_generators.end())
1584  return j->second(rc);
1585  return config();
1586 }
1587 
1588 const std::set<std::string> &reports::report_list()
1589 {
1590  if (!all_reports_.empty()) return all_reports_;
1591  for (const static_report_generators::value_type &v : static_generators) {
1592  all_reports_.insert(v.first);
1593  }
1594  for (const dynamic_report_generators::value_type &v : dynamic_generators_) {
1595  all_reports_.insert(v.first);
1596  }
1597  return all_reports_;
1598 }
const_attack_ptr weapon
The weapon used by the unit to attack the opponent, or nullptr if there is none.
Definition: attack.hpp:49
static config unit_level(const unit *u)
Definition: reports.cpp:229
boost::intrusive_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:30
const gamemap & map() const
Definition: reports.hpp:52
static config unit_xp(const unit *u)
Definition: reports.cpp:462
const t_translation::ter_list & underlying_union_terrain(const map_location &loc) const
Definition: map.cpp:57
std::string tod_dark
const t_translation::ter_list & underlying_def_terrain(const map_location &loc) const
Definition: map.cpp:55
std::vector< double > hp_dist
Resulting probability distribution (might be not as large as max_hp)
int countdown_time() const
Definition: team.hpp:210
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:99
static int get_acceleration()
Definition: types.cpp:524
virtual const map_location & displayed_unit_hex() const
Virtual functions shadowed in game_display.
Definition: display.hpp:254
const team & get_team(int side) const
const t_string & description() const
Definition: terrain.hpp:35
int vision() const
Gets the unit&#39;s vision points.
Definition: unit.hpp:1217
const std::string weapon_details_sep
Definition: constants.cpp:45
This class represents a single unit of a specific type.
Definition: unit.hpp:99
const color_t inactive_details_color
const std::vector< team > & teams() const
Definition: reports.hpp:50
static config time_of_day_at(reports::context &rc, const map_location &mouseover_hex)
Definition: reports.cpp:1092
tod_color color
The color modifications that should be made to the game board to reflect the time of day...
int movement_cost(const t_translation::terrain_code &terrain) const
Get the unit&#39;s movement cost on a particular terrain.
Definition: unit.hpp:1257
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
const color_t GRAY_COLOR
const unit * get_visible_unit(const map_location &loc, const team &current_team, bool see_all=false) const
static config unit_advancement_options(const unit *u)
Definition: reports.cpp:484
std::string get_underlying_terrain_string(const t_translation::terrain_code &terrain) const
Definition: map.cpp:85
const map_location & selected_hex() const
Definition: display.hpp:353
Various functions that implement attacks and attack calculations.
const std::string & id() const
Definition: race.hpp:34
static char const * naps
Definition: reports.cpp:106
const std::string & side_name() const
Definition: team.hpp:306
bool get_state(const std::string &state) const
Check if the unit is affected by a status effect.
Definition: unit.cpp:1290
const color_t GOOD_COLOR
bool terrain_matches(const terrain_code &src, const terrain_code &dest)
Tests whether a specific terrain matches an expression, for matching rules see above.
unit_race::GENDER gender() const
The gender of this unit.
Definition: unit.hpp:381
color_t blue_to_white(int val, bool for_text)
logger & info()
Definition: log.cpp:90
static const int UNREACHABLE
Magic value that signifies a hex is unreachable.
Definition: movetype.hpp:83
int hitpoints() const
The current number of hitpoints this unit has.
Definition: unit.hpp:414
static void add_status(config &r, char const *path, char const *desc1, char const *desc2)
Definition: reports.cpp:75
std::string id
Definition: time_of_day.hpp:91
static config image_report(const std::string &image, const std::string &tooltip="", const std::string &help="")
Definition: reports.cpp:65
bool show_everything() const
Definition: display.hpp:104
const std::set< std::string > & report_list()
Definition: reports.cpp:1588
static std::string format_hp(unsigned hp)
Definition: reports.cpp:853
int lawful_bonus
The % bonus lawful units receive.
Definition: time_of_day.hpp:84
const color_t good_dmg_color
int parry() const
Definition: attack_type.hpp:49
void modified_attacks(bool is_backstab, unsigned &min_attacks, unsigned &max_attacks) const
Calculates the number of attacks this weapon has, considering specials.
Definition: abilities.cpp:782
The unit is poisoned - it loses health each turn.
Definition: unit.hpp:742
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:50
static config text_report(const std::string &text, const std::string &tooltip="", const std::string &help="")
Definition: reports.cpp:57
const terrain_type & get_terrain_info(const t_translation::terrain_code &terrain) const
Definition: map.cpp:96
int viewing_side() const
Definition: display.hpp:133
std::string image
The image to be displayed in the game status.
Definition: time_of_day.hpp:88
static std::string format_prob(double prob)
Definition: reports.cpp:841
The unit is petrified - it cannot move or be attacked.
Definition: unit.hpp:743
const int defense
std::string absolute_image() const
The name of the file to game_display (used in menus).
Definition: unit.cpp:2285
const std::string & type() const
Definition: attack_type.hpp:42
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:360
const t_string & income_description() const
Definition: terrain.hpp:71
const std::string & type_id() const
The id of this unit&#39;s type.
Definition: unit.hpp:288
static config unit_side(reports::context &rc, const unit *u)
Definition: reports.cpp:198
static config unit_name(const unit *u)
Definition: reports.cpp:130
const tod_manager & tod() const
Definition: reports.hpp:56
static config unit_traits(const unit *u)
Definition: reports.cpp:266
report_generator_helper(const char *name, reports::generator_function g)
Definition: reports.cpp:95
static config unit_type(const unit *u)
Definition: reports.cpp:158
static const color_t attack_info_percent_color(int resistance)
Definition: reports.cpp:645
-file sdl_utils.hpp
utils::string_map get_base_resistances() const
Gets resistances without any abilities applied.
Definition: unit.hpp:917
display & screen()
Definition: reports.hpp:55
int num_attacks() const
Definition: attack_type.hpp:51
unit_type::ALIGNMENT alignment() const
The alignment of this unit.
Definition: unit.hpp:391
unsigned int chance_to_hit
Effective chance to hit as a percentage (all factors accounted for).
Definition: attack.hpp:72
A single unit type that the player may recruit.
Definition: types.hpp:42
bool is_nonnull() const
Definition: terrain.hpp:52
std::string get_terrain_string(const map_location &loc) const
Definition: map.cpp:59
const t_string & name(GENDER gender=MALE) const
Definition: race.hpp:36
static config gray_inactive(reports::context &rc, const std::string &str, const std::string &tooltip="")
Definition: reports.cpp:122
t_translation::terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition: map.cpp:292
const terrain_code VOID_TERRAIN
static config unit_alignment(reports::context &rc, const unit *u)
Definition: reports.cpp:330
std::string flag_icon
std::pair< int, map_location > under_leadership(const unit_map &units, const map_location &loc, const_attack_ptr weapon, const_attack_ptr opp_weapon)
Tests if the unit at loc is currently affected by leadership.
Definition: attack.cpp:1619
const color_t weapon_color
#define b
const unit_type & type() const
This unit&#39;s type, accounting for gender and variation.
Definition: unit.hpp:277
static static_report_generators static_generators
Definition: reports.cpp:91
color_t hp_color() const
Color for this unit&#39;s current hitpoints.
Definition: unit.cpp:1074
t_string name
Definition: time_of_day.hpp:89
Object which defines a time of day with associated bonuses, image, sounds etc.
Definition: time_of_day.hpp:57
std::vector< std::string > get_traits_list() const
Gets a list of the traits this unit currently has.
Definition: unit.cpp:868
static config unit_race(const unit *u)
Definition: reports.cpp:179
int defense_modifier(const t_translation::terrain_code &terrain) const
The unit&#39;s defense on a given terrain.
Definition: unit.cpp:1541
std::string span_color(const color_t &color)
Returns a Pango formatting string using the provided color_t object.
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:44
std::string half_signed_value(int val)
Sign with Unicode "−" if negative.
static std::string at(const std::string &file, int line)
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:89
const color_t bad_dmg_color
int damage
Effective damage of the weapon (all factors accounted for).
Definition: attack.hpp:73
const std::string unicode_minus
Definition: constants.cpp:37
const t_string name
bool uses_shroud() const
Definition: team.hpp:316
The unit is slowed - it moves slower and does less damage.
Definition: unit.hpp:741
const terrain_code FOGGED
#define REPORT_GENERATOR(n, cn)
Definition: reports.cpp:101
void register_generator(const std::string &name, generator *)
Definition: reports.cpp:1570
std::ostringstream wrapper.
Definition: formatter.hpp:38
const time_of_day & get_time_of_day(int for_turn=0) const
Returns global time of day for the passed turn.
Definition: tod_manager.hpp:54
bool valid() const
Definition: location.hpp:93
const std::vector< t_string > & trait_descriptions() const
Gets the descriptions of the currently registered traits.
Definition: unit.hpp:961
const std::vector< std::string > advances_to_translated() const
Gets the names of the possible types this unit can advance to on level-up.
Definition: unit.cpp:1136
color_t xp_color() const
Color for this unit&#39;s XP.
Definition: unit.cpp:1084
const t_string & name() const
Definition: attack_type.hpp:40
config generate_report(const std::string &name, context &ct, bool only_static=false)
Definition: reports.cpp:1575
Encapsulates the map of the game.
Definition: map.hpp:36
const t_string & name() const
Gets this unit&#39;s translatable display name.
Definition: unit.hpp:328
static UNUSEDNOWARN std::string _n(const char *str1, const char *str2, int n)
Definition: gettext.hpp:93
const terrain_code MINUS
Computes the statistics of a battle between an attacker and a defender unit.
Definition: attack.hpp:170
const terrain_code PLUS
int max_experience() const
The max number of experience points this unit can have.
Definition: unit.hpp:438
std::set< t_translation::terrain_code > & encountered_terrains()
Definition: game.cpp:931
const std::string & range() const
Definition: attack_type.hpp:44
bool is_enemy(int n) const
Definition: team.hpp:243
void fight(combatant &opponent, bool levelup_considered=true)
Simulate a fight! Can be called multiple times for cumulative calculations.
std::vector< std::pair< t_string, t_string > > special_tooltips(boost::dynamic_bitset<> *active_list=nullptr) const
Returns a vector of names and descriptions for the specials of *this.
Definition: abilities.cpp:714
virtual int playing_side() const
In this case give an obviously wrong answer to fail fast, since this could actually cause a big bug...
Definition: display.hpp:260
int level() const
The current level of this unit.
Definition: unit.hpp:462
static config unit_hp(reports::context &rc, const unit *u)
Definition: reports.cpp:409
Structure describing the statistics of a unit involved in the battle.
Definition: attack.hpp:47
const t_string & type_name() const
Gets the translatable name of this unit&#39;s type.
Definition: unit.hpp:294
std::string small_profile() const
An optional profile image to display in Help.
Definition: unit.cpp:1013
const color_t YELLOW_COLOR
static UNUSEDNOWARN std::string gettext(const char *str)
Definition: gettext.hpp:66
const std::string unicode_figure_dash
Definition: constants.cpp:40
std::function< config(reports::context &)> generator_function
Definition: reports.hpp:80
std::string flag_rgb
static std::string get_side_color_id(unsigned side)
Definition: team.cpp:962
static config unit_status(reports::context &rc, const unit *u)
Definition: reports.cpp:296
const std::vector< t_string > & trait_names() const
Gets the names of the currently registered traits.
Definition: unit.hpp:951
Encapsulates the map of the game.
Definition: location.hpp:42
static const unit * get_visible_unit(reports::context &rc)
Definition: reports.cpp:108
int round_damage(int base_damage, int bonus, int divisor)
round (base_damage * bonus / divisor) to the closest integer, but up or down towards base_damage ...
Definition: math.hpp:59
unsigned swarm_blows(unsigned min_blows, unsigned max_blows, unsigned hp, unsigned max_hp)
Calculates the number of blows resulting from swarm.
Definition: attack.hpp:37
static config unit_box_at(reports::context &rc, const map_location &mouseover_hex)
Definition: reports.cpp:1143
bool invisible(const map_location &loc, bool see_all=true) const
Definition: unit.cpp:2317
All combat-related info.
const color_t weapon_details_color
bool shrouded(const map_location &loc) const
Definition: team.cpp:644
const time_of_day get_illuminated_time_of_day(const unit_map &units, const gamemap &map, const map_location &loc, int for_turn=0) const
Returns time of day object for the passed turn at a location.
std::string escape_text(const std::string &text)
Escapes the pango markup characters in a text.
Definition: escape.hpp:32
static config unit_moves(reports::context &rc, const unit *u)
Definition: reports.cpp:580
std::size_t i
Definition: function.cpp:933
const display_context & dc() const
Definition: reports.hpp:54
const std::string unicode_en_dash
Definition: constants.cpp:38
int damage() const
Definition: attack_type.hpp:50
std::vector< std::tuple< std::string, t_string, t_string, t_string > > ability_tooltips(boost::dynamic_bitset<> *active_list=nullptr) const
Gets the names and descriptions of this unit&#39;s abilities.
Definition: abilities.cpp:276
int max_hitpoints() const
The max number of hitpoints this unit can have.
Definition: unit.hpp:420
int accuracy() const
Definition: attack_type.hpp:48
std::string resistance_color(const int resistance)
Definition: helper.cpp:35
static const unit * get_selected_unit(reports::context &rc)
Definition: reports.cpp:115
static void add_image(config &report, const std::string &image, const std::string &tooltip, const std::string &help="")
Definition: reports.cpp:48
static map_location::DIRECTION s
bool show_variations_in_help() const
Whether the unit type has at least one help-visible variation.
Definition: types.cpp:775
const std::string & flag_icon() const
Definition: team.hpp:301
const unit_map & units() const
Definition: reports.hpp:51
t_translation::terrain_code number() const
Definition: terrain.hpp:44
const t_string & income_description_own() const
Definition: terrain.hpp:74
double g
Definition: astarsearch.cpp:63
std::string to_hex_string() const
Returns the stored color in rrggbb hex format.
Definition: color.cpp:97
std::string signed_percent(int val)
Convert into a percentage (using the Unicode "−" and +0% convention.
const t_string & income_description_enemy() const
Definition: terrain.hpp:73
attack_itors attacks()
Gets an iterator over this unit&#39;s attacks.
Definition: unit.hpp:806
std::map< std::string, std::string > advancement_icons() const
Gets and image path and and associated description for each advancement option.
Definition: unit.cpp:1609
std::string accuracy_parry_description() const
Definition: attack_type.cpp:78
static std::string flush(std::ostringstream &s)
Definition: reports.cpp:83
int modified_damage(bool is_backstab) const
Returns the damage per attack of this weapon, considering specials.
Definition: abilities.cpp:805
const t_translation::ter_list & union_type() const
Definition: terrain.hpp:50
const unit_race * race() const
Gets this unit&#39;s race.
Definition: unit.hpp:408
#define N_(String)
Definition: gettext.hpp:97
const std::string unicode_bullet
Definition: constants.cpp:42
static int sort(lua_State *L)
Definition: ltablib.cpp:411
std::string observer
bool is_fearless() const
Gets whether this unit is fearless - ie, unaffected by time of day.
Definition: unit.hpp:1071
static config unit_defense(reports::context &rc, const unit *u, const map_location &displayed_unit_hex)
Definition: reports.cpp:505
config & add_child(config_key_type key)
Definition: config.cpp:479
const t_string & name() const
Definition: terrain.hpp:33
std::string base_name(const std::string &file, const bool remove_extension)
Returns the base filename of a file, with directory name stripped.
std::map< std::string, reports::generator_function > static_report_generators
Definition: reports.cpp:90
static int attack_info(reports::context &rc, const attack_type &at, config &res, const unit &u, const map_location &displayed_unit_hex)
Definition: reports.cpp:653
bool is_village(const map_location &loc) const
Definition: map.cpp:64
const std::string & color() const
Definition: team.hpp:256
const ter_match ALL_OFF_MAP
const std::string weapon_numbers_sep
Definition: constants.cpp:44
double t
Definition: astarsearch.cpp:63
int experience() const
The current number of experience points this unit has.
Definition: unit.hpp:432
color_t red_to_green(int val, bool for_text)
Return a color corresponding to the value val red for val=0 to green for val=100, passing by yellow...
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1174
symbol_table string_table
Definition: language.cpp:63
std::size_t viewing_team() const
The viewing team is the team currently viewing the game.
Definition: display.hpp:128
static void add_text(config &report, const std::string &text, const std::string &tooltip, const std::string &help="")
Definition: reports.cpp:39
this module manages the cache of images.
std::string tod_bright
const std::string & id() const
Definition: terrain.hpp:37
std::vector< terrain_code > ter_list
Definition: translation.hpp:78
specials_context_t specials_context_for_listing(bool attacking=true) const
t_string unit_description() const
A detailed description of this unit.
Definition: unit.hpp:375
int total_movement() const
The maximum moves this unit has.
Definition: unit.hpp:1090
unsigned int num_blows
Effective number of blows, takes swarm into account.
Definition: attack.hpp:77
std::string base_str() const
Definition: tstring.hpp:190
bool fogged(const map_location &loc) const
Definition: team.cpp:653
const t_string & income_description_ally() const
Definition: terrain.hpp:72
static config unit_weapons(reports::context &rc, const unit *attacker, const map_location &attacker_pos, const unit *defender, bool show_attacker)
Definition: reports.cpp:860
int side() const
The side this unit belongs to.
Definition: unit.hpp:265
static void reverse(lua_State *L, StkId from, StkId to)
Definition: lapi.cpp:193
specials_context_t specials_context(unit_const_ptr self, unit_const_ptr other, const map_location &unit_loc, const map_location &other_loc, bool attacking, const_attack_ptr other_attack) const
std::string signed_value(int val)
Convert into a signed value (using the Unicode "−" and +0 convention.
int resistance_against(const std::string &damage_name, bool attacker, const map_location &loc, const_attack_ptr weapon=nullptr, const_attack_ptr opp_weapon=nullptr) const
The unit&#39;s resistance against a given damage type.
Definition: unit.cpp:1584
std::vector< std::pair< std::string, std::string > > amla_icons() const
Gets the image and description data for modification advancements.
Definition: unit.cpp:1649
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
static std::string alignment_description(ALIGNMENT align, unit_race::GENDER gender=unit_race::MALE)
mock_char c
static config unit_abilities(const unit *u)
Definition: reports.cpp:363
std::shared_ptr< const attack_type > const_attack_ptr
Definition: ptr.hpp:37
static config unit_vision(const unit *u)
Definition: reports.cpp:559
int bonus_modified
Definition: time_of_day.hpp:85
const std::set< map_location > & villages() const
Definition: team.hpp:184
std::string path
File path.
bool use_twelve_hour_clock_format()
Definition: general.cpp:906
int movement_left() const
Gets how far a unit can move, considering the incapacitated flag.
Definition: unit.hpp:1100
std::string image_mods() const
Gets an IPF string containing all IPF image mods.
Definition: unit.cpp:2468
const std::string & icon_image() const
Definition: terrain.hpp:29
const color_t BAD_COLOR
int combat_modifier(const unit_map &units, const gamemap &map, const map_location &loc, unit_type::ALIGNMENT alignment, bool is_fearless)
Returns the amount that a unit&#39;s damage should be multiplied by due to the current time of day...
Definition: attack.cpp:1630
const color_t inactive_ability_color