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