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