The Battle for Wesnoth  1.17.6+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 map_location& mouseover_hex = rc.screen().mouseover_hex();
448 
449  return unit_abilities(u, mouseover_hex);
450 }
451 REPORT_GENERATOR(selected_unit_abilities, rc)
452 {
453  const unit *u = get_selected_unit(rc);
454 
455  const map_location& mouseover_hex = rc.screen().mouseover_hex();
456  const unit *visible_unit = get_visible_unit(rc);
457  if(visible_unit && u && visible_unit->id() != u->id() && mouseover_hex.valid())
458  return unit_abilities(u, mouseover_hex);
459  else
460  return unit_abilities(u, u->get_location());
461 }
462 
463 
464 static config unit_hp(reports::context& rc, const unit* u)
465 {
466  if (!u) return config();
467  std::ostringstream str, tooltip;
468  str << span_color(u->hp_color()) << u->hitpoints()
469  << '/' << u->max_hitpoints() << naps;
470 
471  std::vector<std::string> resistances_table;
472 
473  bool att_def_diff = false;
474  map_location displayed_unit_hex = rc.screen().displayed_unit_hex();
475  for (const utils::string_map_res::value_type &resist : u->get_base_resistances())
476  {
477  std::ostringstream line;
478  line << translation::gettext(resist.first.c_str()) << ": ";
479  // Some units have different resistances when attacking or defending.
480  int res_att = 100 - u->resistance_against(resist.first, true, displayed_unit_hex);
481  int res_def = 100 - u->resistance_against(resist.first, false, displayed_unit_hex);
482  const std::string def_color = unit_helper::resistance_color(res_def);
483  if (res_att == res_def) {
484  line << "<span foreground=\"" << def_color << "\">" << utils::signed_percent(res_def)
485  << naps << '\n';
486  } else {
487  const std::string att_color = unit_helper::resistance_color(res_att);
488  line << "<span foreground=\"" << att_color << "\">" << utils::signed_percent(res_att)
489  << naps << "/"
490  << "<span foreground=\"" << def_color << "\">" << utils::signed_percent(res_def)
491  << naps << '\n';
492  att_def_diff = true;
493  }
494  resistances_table.push_back(line.str());
495  }
496 
497  tooltip << _("Resistances: ");
498  if (att_def_diff)
499  tooltip << _("(Att / Def)");
500  tooltip << '\n';
501  for (const std::string &line : resistances_table) {
502  tooltip << line;
503  }
504  return text_report(str.str(), tooltip.str());
505 }
507 {
508  const unit *u = get_visible_unit(rc);
509  return unit_hp(rc, u);
510 }
511 REPORT_GENERATOR(selected_unit_hp, rc)
512 {
513  const unit *u = get_selected_unit(rc);
514  return unit_hp(rc, u);
515 }
516 
517 static config unit_xp(const unit* u)
518 {
519  if (!u) return config();
520  std::ostringstream str, tooltip;
521  str << span_color(u->xp_color());
522  if(u->can_advance()) {
523  str << u->experience() << '/' << u->max_experience();
524  } else {
525  str << font::unicode_en_dash;
526  }
527  str << naps;
528 
530  tooltip << _("Experience Modifier: ") << exp_mod << '%';
531  return text_report(str.str(), tooltip.str());
532 }
534 {
535  const unit *u = get_visible_unit(rc);
536  return unit_xp(u);
537 }
538 REPORT_GENERATOR(selected_unit_xp, rc)
539 {
540  const unit *u = get_selected_unit(rc);
541  return unit_xp(u);
542 }
543 
545 {
546  if (!u) return config();
547  config res;
548  for (const auto& ps : u->advancement_icons()) {
549  add_image(res, ps.first, ps.second);
550  }
551  return res;
552 }
554 {
555  const unit *u = get_visible_unit(rc);
556  return unit_advancement_options(u);
557 }
558 REPORT_GENERATOR(selected_unit_advancement_options, rc)
559 {
560  const unit *u = get_selected_unit(rc);
561  return unit_advancement_options(u);
562 }
563 
564 static config unit_defense(reports::context & rc, const unit* u, const map_location& displayed_unit_hex)
565 {
566  if(!u) {
567  return config();
568  }
569 
570  std::ostringstream str, tooltip;
571  const gamemap &map = rc.map();
572  if(!rc.map().on_board(displayed_unit_hex)) {
573  return config();
574  }
575 
576  const t_translation::terrain_code &terrain = map[displayed_unit_hex];
577  int def = 100 - u->defense_modifier(terrain);
578  color_t color = game_config::red_to_green(def);
579  str << span_color(color) << def << '%' << naps;
580  tooltip << _("Terrain: ") << "<b>" << map.get_terrain_info(terrain).description() << "</b>\n";
581 
582  const t_translation::ter_list &underlyings = map.underlying_def_terrain(terrain);
583  if (underlyings.size() != 1 || underlyings.front() != terrain)
584  {
585  bool revert = false;
586  for (const t_translation::terrain_code &t : underlyings)
587  {
588  if (t == t_translation::MINUS) {
589  revert = true;
590  } else if (t == t_translation::PLUS) {
591  revert = false;
592  } else {
593  int t_def = 100 - u->defense_modifier(t);
594  color_t t_color = game_config::red_to_green(t_def);
595  tooltip << '\t' << map.get_terrain_info(t).description() << ": "
596  << span_color(t_color) << t_def << '%' << naps
597  << (revert ? _("maximum^max.") : _("minimum^min.")) << '\n';
598  }
599  }
600  }
601 
602  tooltip << "<b>" << _("Defense: ") << span_color(color) << def << '%' << naps << "</b>";
603  const std::string has_variations_prefix = (u->type().show_variations_in_help() ? ".." : "");
604  return text_report(str.str(), tooltip.str(), has_variations_prefix + "unit_" + u->type_id());
605 }
607 {
608  const unit *u = get_visible_unit(rc);
609  const team &viewing_team = rc.teams()[rc.screen().viewing_team()];
610  const map_location& mouseover_hex = rc.screen().mouseover_hex();
611  const map_location& displayed_unit_hex = rc.screen().displayed_unit_hex();
612  const map_location& hex = (mouseover_hex.valid() && !viewing_team.shrouded(mouseover_hex)) ? mouseover_hex : displayed_unit_hex;
613  return unit_defense(rc, u, hex);
614 }
615 REPORT_GENERATOR(selected_unit_defense, rc)
616 {
617  const unit *u = get_selected_unit(rc);
618  const map_location& attack_indicator_src = game_display::get_singleton()->get_attack_indicator_src();
619  if(attack_indicator_src.valid())
620  return unit_defense(rc, u, attack_indicator_src);
621 
622  const map_location& mouseover_hex = rc.screen().mouseover_hex();
623  const unit *visible_unit = get_visible_unit(rc);
624  if(visible_unit && u && visible_unit->id() != u->id() && mouseover_hex.valid())
625  return unit_defense(rc, u, mouseover_hex);
626  else
627  return unit_defense(rc, u, u->get_location());
628 }
629 
630 static config unit_vision(const unit* u)
631 {
632  if (!u) return config();
633 
634  // TODO
635  std::ostringstream str, tooltip;
636  if (u->vision() != u->total_movement()) {
637  str << _("vision:") << ' ' << u->vision();
638  tooltip << _("vision:") << ' ' << u->vision() << '\n';
639  }
640  if (u->jamming() != 0) {
641  if (static_cast<std::streamoff>(str.tellp()) == 0)
642  str << _("jamming:") << ' ' << u->jamming();
643  tooltip << _("jamming:") << ' ' << u->jamming() << '\n';
644  }
645  return text_report(str.str(), tooltip.str());
646 }
648 {
649  const unit* u = get_visible_unit(rc);
650  return unit_vision(u);
651 }
652 REPORT_GENERATOR(selected_unit_vision, rc)
653 {
654  const unit* u = get_selected_unit(rc);
655  return unit_vision(u);
656 }
657 
658 static config unit_moves(reports::context & rc, const unit* u, bool is_visible_unit)
659 {
660  if (!u) return config();
661  std::ostringstream str, tooltip;
662  double movement_frac = 1.0;
663 
664  std::set<terrain_movement> terrain_moves;
665 
666  if (u->side() == rc.screen().playing_side()) {
667  movement_frac = static_cast<double>(u->movement_left()) / std::max<int>(1, u->total_movement());
668  if (movement_frac > 1.0)
669  movement_frac = 1.0;
670  }
671 
672  tooltip << _("Movement Costs:") << "\n";
675  continue;
676 
677  const terrain_type& info = rc.map().get_terrain_info(terrain);
678 
679  if (info.union_type().size() == 1 && info.union_type()[0] == info.number() && info.is_nonnull()) {
680  terrain_moves.emplace(info.name(), u->movement_cost(terrain));
681  }
682  }
683 
684  for (const terrain_movement& tm : terrain_moves) {
685  tooltip << tm.name << ": ";
686 
687  //movement - range: 1 .. 5, movetype::UNREACHABLE=impassable
688  const bool cannot_move = tm.moves > u->total_movement(); // cannot move in this terrain
689  double movement_red_to_green = 100.0 - 25.0 * tm.moves;
690 
691  // passing false to select the more saturated red-to-green scale
692  std::string color = game_config::red_to_green(movement_red_to_green, false).to_hex_string();
693  tooltip << "<span foreground=\"" << color << "\">";
694  // A 5 MP margin; if the movement costs go above
695  // the unit's max moves + 5, we replace it with dashes.
696  if (cannot_move && (tm.moves > u->total_movement() + 5)) {
697  tooltip << font::unicode_figure_dash;
698  } else if (cannot_move) {
699  tooltip << "(" << tm.moves << ")";
700  } else {
701  tooltip << tm.moves;
702  }
703  if(tm.moves != 0) {
704  const int movement_hexes_per_turn = u->total_movement() / tm.moves;
705  tooltip << " ";
706  for(int i = 0; i < movement_hexes_per_turn; ++i) {
707  tooltip << "\u2b23"; // Unicode horizontal black hexagon
708  }
709  }
710  tooltip << naps << '\n';
711  }
712 
713  int grey = 128 + static_cast<int>((255 - 128) * movement_frac);
714  color_t c = color_t(grey, grey, grey);
715  int numerator = u->movement_left();
716  if(is_visible_unit) {
718  if(route.steps.size() > 0) {
719  numerator -= route.move_cost;
720  if(numerator < 0) {
721  // Multi-turn move
722  // TODO: what to show in this case?
723  numerator = 0;
724  }
725 
726  // If the route captures a village, assume that uses up all remaining MP.
727  const auto& end = route.marks.find(route.steps.back());
728  if(end != route.marks.end() && end->second.capture) {
729  numerator = 0;
730  }
731  } else {
732  // TODO: if the mouseover hex is unreachable (for example, a deep water hex
733  // and the current unit is a land unit), what to show?
734  }
735  }
736  str << span_color(c) << numerator << '/' << u->total_movement() << naps;
737  return text_report(str.str(), tooltip.str());
738 }
740 {
741  const unit *u = get_visible_unit(rc);
742  return unit_moves(rc, u, true);
743 }
744 REPORT_GENERATOR(selected_unit_moves, rc)
745 {
746  const unit *u = get_selected_unit(rc);
747  return unit_moves(rc, u, false);
748 }
749 
750 /**
751  * Maps resistance <= -60 (resistance value <= -60%) to intense red.
752  * Maps resistance >= 60 (resistance value >= 60%) to intense green.
753  * Intermediate values are affinely mapped to the red-to-green scale,
754  * with 0 (0%) being mapped to yellow.
755  * Compare unit_helper::resistance_color().
756  */
757 static inline const color_t attack_info_percent_color(int resistance)
758 {
759  // Passing false to select the more saturated red-to-green scale.
760  return game_config::red_to_green(50.0 + resistance * 5.0 / 6.0, false);
761 }
762 
763 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)
764 {
765  std::ostringstream str, tooltip;
766  int damage = 0;
767 
768  struct string_with_tooltip {
769  std::string str;
770  std::string tooltip;
771  };
772 
773  {
774  auto ctx = at.specials_context(u.shared_from_this(), hex, u.side() == rc.screen().playing_side());
775  int base_damage = at.damage();
776  int specials_damage = at.modified_damage(false);
777  int damage_multiplier = 100;
778  const_attack_ptr weapon = at.shared_from_this();
779  int tod_bonus = combat_modifier(get_visible_time_of_day_at(rc, hex), u.alignment(), u.is_fearless());
780  damage_multiplier += tod_bonus;
781  int leader_bonus = under_leadership(u, hex, weapon);
782  if (leader_bonus != 0)
783  damage_multiplier += leader_bonus;
784 
786  int damage_divisor = slowed ? 20000 : 10000;
787  // Assume no specific resistance (i.e. multiply by 100).
788  damage = round_damage(specials_damage, damage_multiplier * 100, damage_divisor);
789 
790  // Hit points are used to calculate swarm, so they need to be bounded.
791  unsigned max_hp = u.max_hitpoints();
792  unsigned cur_hp = std::min<unsigned>(std::max(0, u.hitpoints()), max_hp);
793 
794  unsigned base_attacks = at.num_attacks();
795  unsigned min_attacks, max_attacks;
796  at.modified_attacks(false, min_attacks, max_attacks);
797  unsigned num_attacks = swarm_blows(min_attacks, max_attacks, cur_hp, max_hp);
798 
799  color_t dmg_color = font::weapon_color;
800  if ( damage > specials_damage )
801  dmg_color = font::good_dmg_color;
802  else if ( damage < specials_damage )
803  dmg_color = font::bad_dmg_color;
804 
805  str << span_color(dmg_color) << " " << damage << naps << span_color(font::weapon_color)
806  << font::weapon_numbers_sep << num_attacks << ' ' << at.name()
807  << "</span>\n";
808  tooltip << _("Weapon: ") << "<b>" << at.name() << "</b>\n"
809  << _("Damage: ") << "<b>" << damage << "</b>\n";
810 
811  if ( tod_bonus || leader_bonus || slowed || specials_damage != base_damage )
812  {
813  tooltip << '\t' << _("Base damage: ") << base_damage << '\n';
814  if ( specials_damage != base_damage ) {
815  tooltip << '\t' << _("With specials: ") << specials_damage << '\n';
816  }
817  if (tod_bonus) {
818  tooltip << '\t' << _("Time of day: ")
819  << utils::signed_percent(tod_bonus) << '\n';
820  }
821  if (leader_bonus) {
822  tooltip << '\t' << _("Leadership: ")
823  << utils::signed_percent(leader_bonus) << '\n';
824  }
825  if (slowed) {
826  tooltip << '\t' << _("Slowed: ") << "/ 2" << '\n';
827  }
828  }
829 
830  tooltip << _("Attacks: ") << "<b>" << num_attacks << "</b>\n";
831  if ( max_attacks != min_attacks && cur_hp != max_hp ) {
832  if ( max_attacks < min_attacks ) {
833  // "Reverse swarm"
834  tooltip << '\t' << _("Max swarm bonus: ") << (min_attacks-max_attacks) << '\n';
835  tooltip << '\t' << _("Swarm: ") << "* "<< (100 - cur_hp*100/max_hp) << "%\n";
836  tooltip << '\t' << _("Base attacks: ") << '+' << base_attacks << '\n';
837  // The specials line will not necessarily match up with how the
838  // specials are calculated, but for an unusual case, simple brevity
839  // trumps complexities.
840  if ( max_attacks != base_attacks ) {
841  int attack_diff = static_cast<int>(max_attacks) - static_cast<int>(base_attacks);
842  tooltip << '\t' << _("Specials: ") << utils::signed_value(attack_diff) << '\n';
843  }
844  }
845  else {
846  // Regular swarm
847  tooltip << '\t' << _("Base attacks: ") << base_attacks << '\n';
848  if ( max_attacks != base_attacks ) {
849  tooltip << '\t' << _("With specials: ") << max_attacks << '\n';
850  }
851  if ( min_attacks != 0 ) {
852  tooltip << '\t' << _("Subject to swarm: ") << (max_attacks-min_attacks) << '\n';
853  }
854  tooltip << '\t' << _("Swarm: ") << "* "<< (cur_hp*100/max_hp) << "%\n";
855  }
856  }
857  else if ( num_attacks != base_attacks ) {
858  tooltip << '\t' << _("Base attacks: ") << base_attacks << '\n';
859  tooltip << '\t' << _("With specials: ") << num_attacks << '\n';
860  }
861 
862  const string_with_tooltip damage_and_num_attacks {flush(str), flush(tooltip)};
863 
864  std::string range = string_table["range_" + at.range()];
865  std::string lang_type = string_table["type_" + at.type()];
866 
867  // SCALE_INTO() is needed in case the 72x72 images/misc/missing-image.png is substituted.
868  const std::string range_png = std::string("icons/profiles/") + at.range() + "_attack.png~SCALE_INTO(16,16)";
869  const std::string type_png = std::string("icons/profiles/") + at.type() + ".png~SCALE_INTO(16,16)";
870  const bool range_png_exists = image::locator(range_png).file_exists();
871  const bool type_png_exists = image::locator(type_png).file_exists();
872 
873  if(!range_png_exists || !type_png_exists) {
874  str << span_color(font::weapon_details_color) << " " << " "
875  << range << font::weapon_details_sep
876  << lang_type << "</span>\n";
877  }
878 
879  tooltip << _("Weapon range: ") << "<b>" << range << "</b>\n"
880  << _("Damage type: ") << "<b>" << lang_type << "</b>\n"
881  << _("Damage versus: ") << '\n';
882 
883  // Show this weapon damage and resistance against all the different units.
884  // We want weak resistances (= good damage) first.
885  std::map<int, std::set<std::string>, std::greater<int>> resistances;
886  std::set<std::string> seen_types;
887  const team &unit_team = rc.dc().get_team(u.side());
888  const team &viewing_team = rc.teams()[rc.screen().viewing_team()];
889  for (const unit &enemy : rc.units())
890  {
891  if (enemy.incapacitated()) //we can't attack statues so don't display them in this tooltip
892  continue;
893  if (!unit_team.is_enemy(enemy.side()))
894  continue;
895  const map_location &loc = enemy.get_location();
896  const bool see_all = game_config::debug || rc.screen().show_everything();
897  if (!enemy.is_visible_to_team(viewing_team, see_all))
898  continue;
899  bool new_type = seen_types.insert(enemy.type_id()).second;
900  if (new_type) {
901  int resistance = enemy.resistance_against(at, false, loc);
902  resistances[resistance].insert(enemy.type_name());
903  }
904  }
905 
906  for (const auto& resist : resistances) {
907  int damage_with_resistance = round_damage(specials_damage, damage_multiplier * resist.first, damage_divisor);
908  tooltip << "<b>" << damage_with_resistance << "</b> "
909  << "<span color='" << attack_info_percent_color(resist.first-100).to_hex_string() << "'>"
910  << "<i>(" << utils::signed_percent(resist.first-100) << ")</i>"
911  << naps
912  << " : \t" // spaces to align the tab to a multiple of 8
913  << utils::join(resist.second, " " + font::unicode_bullet + " ") << '\n';
914  }
915  const string_with_tooltip damage_versus {flush(str), flush(tooltip)};
916 
917 #if 0
918  // We wanted to use the attack icon here, but couldn't find a good layout.
919  // The default images are 60x60 and have a 2-pixel transparent border. Trim it.
920  // The first SCALE() accounts for theoretically possible add-ons attack images larger than 60x60.
921  const std::string attack_icon = at.icon() + "~SCALE_INTO_SHARP(60,60)~CROP(2,2,56,56)~SCALE_INTO_SHARP(32,32)";
922  add_image(res, attack_icon, at.name());
923  add_text(res, " ", "");
924 #endif
925 
926  // The icons are 16x16. We add 5px padding for alignment reasons (placement of the icon in relation to ascender and descender letters).
927  const std::string spacer = "misc/blank.png~CROP(0, 0, 16, 21)"; // 21 == 16+5
928  add_image(res, spacer + "~BLIT(" + range_png + ",0,5)", damage_versus.tooltip);
929  add_image(res, spacer + "~BLIT(" + type_png + ",0,5)", damage_versus.tooltip);
930  add_text(res, damage_and_num_attacks.str, damage_and_num_attacks.tooltip);
931  add_text(res, damage_versus.str, damage_versus.tooltip); // This string is usually empty
932 
933  const std::string &accuracy_parry = at.accuracy_parry_description();
934  if (!accuracy_parry.empty())
935  {
937  << " " << accuracy_parry << "</span>\n";
938  int accuracy = at.accuracy();
939  if (accuracy) {
940  tooltip << _("Accuracy:") << "<b>"
941  << utils::signed_percent(accuracy) << "</b>\n";
942  }
943  int parry = at.parry();
944  if (parry) {
945  tooltip << _("Parry:") << "<b>"
946  << utils::signed_percent(parry) << "</b>\n";
947  }
948  add_text(res, flush(str), flush(tooltip));
949  }
950  }
951 
952  {
953  //If we have a second unit, do the 2-unit specials_context
954  bool attacking = (u.side() == rc.screen().playing_side());
955  auto ctx = (sec_u == nullptr) ? at.specials_context_for_listing(attacking) :
956  at.specials_context(u.shared_from_this(), sec_u->shared_from_this(), hex, sec_u->get_location(), attacking, sec_u_weapon);
957 
958  boost::dynamic_bitset<> active;
959  const std::vector<std::pair<t_string, t_string>> &specials = at.special_tooltips(&active);
960  const std::size_t specials_size = specials.size();
961  for ( std::size_t i = 0; i != specials_size; ++i )
962  {
963  // Aliases for readability:
964  const t_string &name = specials[i].first;
965  const t_string &description = specials[i].second;
966  const color_t &details_color = active[i] ? font::weapon_details_color :
968 
969  str << span_color(details_color) << " " << " " << name << naps << '\n';
970  std::string help_page = "weaponspecial_" + name.base_str();
971  tooltip << _("Weapon special: ") << "<b>" << name << "</b>";
972  if ( !active[i] )
973  tooltip << "<i>" << _(" (inactive)") << "</i>";
974  tooltip << '\n' << description;
975 
976  add_text(res, flush(str), flush(tooltip), help_page);
977  }
978  if(!specials.empty()) {
979  // Add some padding so the end of the specials list
980  // isn't too close vertically to the attack icons of
981  // the next attack. Also for symmetry with the padding
982  // above the list of specials (below the attack icon line).
983  const std::string spacer = "misc/blank.png~CROP(0, 0, 1, 5)";
984  add_image(res, spacer, "");
985  add_text(res, "\n", "");
986  }
987  }
988  return damage;
989 }
990 
991 // Conversion routine for both unscathed and damage change percentage.
992 static std::string format_prob(double prob)
993 {
994  if(prob > 0.9995) {
995  return "100%";
996  } else if(prob < 0.0005) {
997  return "0%";
998  }
999  std::ostringstream res;
1000  res << std::setprecision(prob < 0.01 ? 1 : prob < 0.1 ? 2 : 3) << 100.0 * prob << "%";
1001  return res.str();
1002 }
1003 
1004 static std::string format_hp(unsigned hp)
1005 {
1006  std::ostringstream res;
1007  res << ' ' << std::setw(3) << hp;
1008  return res.str();
1009 }
1010 
1011 static config unit_weapons(reports::context & rc, unit_const_ptr attacker, const map_location &attacker_pos, const unit *defender, bool show_attacker)
1012 {
1013  if (!attacker || !defender) return config();
1014 
1015  const unit* u = show_attacker ? attacker.get() : defender;
1016  const unit* sec_u = !show_attacker ? attacker.get() : defender;
1017  const map_location unit_loc = show_attacker ? attacker_pos : defender->get_location();
1018 
1019  std::ostringstream str, tooltip;
1020  config res;
1021 
1022  std::vector<battle_context> weapons;
1023  for (unsigned int i = 0; i < attacker->attacks().size(); i++) {
1024  // skip weapons with attack_weight=0
1025  if (attacker->attacks()[i].attack_weight() > 0) {
1026  weapons.emplace_back(rc.units(), attacker_pos, defender->get_location(), i, -1, 0.0, nullptr, attacker);
1027  }
1028  }
1029 
1030  for (const battle_context& weapon : weapons) {
1031 
1032  // Predict the battle outcome.
1033  combatant attacker_combatant(weapon.get_attacker_stats());
1034  combatant defender_combatant(weapon.get_defender_stats());
1035  attacker_combatant.fight(defender_combatant);
1036 
1037  const battle_context_unit_stats& context_unit_stats =
1038  show_attacker ? weapon.get_attacker_stats() : weapon.get_defender_stats();
1039  const battle_context_unit_stats& other_context_unit_stats =
1040  !show_attacker ? weapon.get_attacker_stats() : weapon.get_defender_stats();
1041 
1042  int total_damage = 0;
1043  int base_damage = 0;
1044  int num_blows = 0;
1045  int chance_to_hit = 0;
1046  t_string weapon_name = _("weapon^None");
1047 
1048  color_t dmg_color = font::weapon_color;
1049  if (context_unit_stats.weapon) {
1050  base_damage = attack_info(rc, *context_unit_stats.weapon, res, *u, unit_loc, sec_u, other_context_unit_stats.weapon);
1051  total_damage = context_unit_stats.damage;
1052  num_blows = context_unit_stats.num_blows;
1053  chance_to_hit = context_unit_stats.chance_to_hit;
1054  weapon_name = context_unit_stats.weapon->name();
1055 
1056  if ( total_damage > base_damage )
1057  dmg_color = font::good_dmg_color;
1058  else if ( total_damage < base_damage )
1059  dmg_color = font::bad_dmg_color;
1060  } else {
1061  str << span_color(font::weapon_color) << weapon_name << naps << "\n";
1062  tooltip << _("Weapon: ") << "<b>" << weapon_name << "</b>\n"
1063  << _("Damage: ") << "<b>" << "0" << "</b>\n";
1064  }
1065 
1066  color_t chance_color = game_config::red_to_green(chance_to_hit);
1067 
1068  // Total damage.
1069  str << " " << span_color(dmg_color) << total_damage << naps << span_color(font::weapon_color)
1070  << font::unicode_en_dash << num_blows
1071  << " (" << span_color(chance_color) << chance_to_hit << "%" << naps << ")"
1072  << naps << "\n";
1073 
1074  tooltip << _("Weapon: ") << "<b>" << weapon_name << "</b>\n"
1075  << _("Total damage") << "<b>" << total_damage << "</b>\n";
1076 
1077  // Create the hitpoints distribution.
1078  std::vector<std::pair<int, double>> hp_prob_vector;
1079 
1080  // First, we sort the probabilities in ascending order.
1081  std::vector<std::pair<double, int>> prob_hp_vector;
1082  int i;
1083 
1084  combatant* c = show_attacker ? &attacker_combatant : &defender_combatant;
1085 
1086  for(i = 0; i < static_cast<int>(c->hp_dist.size()); i++) {
1087  double prob = c->hp_dist[i];
1088 
1089  // We keep only values above 0.1%.
1090  if(prob > 0.001)
1091  prob_hp_vector.emplace_back(prob, i);
1092  }
1093 
1094  std::sort(prob_hp_vector.begin(), prob_hp_vector.end());
1095 
1096  //TODO fendrin -- make that dynamically
1097  int max_hp_distrib_rows_ = 10;
1098 
1099  // We store a few of the highest probability hitpoint values.
1100  int nb_elem = std::min<int>(max_hp_distrib_rows_, prob_hp_vector.size());
1101 
1102  for(i = prob_hp_vector.size() - nb_elem;
1103  i < static_cast<int>(prob_hp_vector.size()); i++) {
1104 
1105  hp_prob_vector.emplace_back(prob_hp_vector[i].second, prob_hp_vector[i].first);
1106  }
1107 
1108  // Then, we sort the hitpoint values in ascending order.
1109  std::sort(hp_prob_vector.begin(), hp_prob_vector.end());
1110  // And reverse the order. Might be doable in a better manor.
1111  std::reverse(hp_prob_vector.begin(), hp_prob_vector.end());
1112 
1113  for(i = 0; i < static_cast<int>(hp_prob_vector.size()); i++) {
1114 
1115  int hp = hp_prob_vector[i].first;
1116  double prob = hp_prob_vector[i].second;
1117  color_t prob_color = game_config::blue_to_white(prob * 100.0, true);
1118 
1119  str << span_color(font::weapon_details_color) << " " << " "
1120  << span_color(u->hp_color(hp)) << format_hp(hp) << naps
1121  << " " << font::weapon_numbers_sep << " "
1122  << span_color(prob_color) << format_prob(prob) << naps
1123  << naps << "\n";
1124  }
1125 
1126  add_text(res, flush(str), flush(tooltip));
1127  }
1128  return res;
1129 }
1130 
1131 /*
1132  * Display the attacks of the displayed unit against the unit passed as argument.
1133  * 'hex' is the location the attacker will be at during combat.
1134  */
1135 static config unit_weapons(reports::context & rc, const unit *u, const map_location &hex)
1136 {
1137  config res = config();
1138  if ((u != nullptr) && (!u->attacks().empty())) {
1139  const std::string attack_headline = _n("Attack", "Attacks", u->attacks().size());
1140 
1142  + attack_headline + "</span>" + '\n', "");
1143 
1144  const auto left = u->attacks_left(false), max = u->max_attacks();
1145  if(max != 1) {
1146  // TRANSLATORS: This string is shown in the sidebar beneath the word "Attacks" when a unit can attack multiple times per turn
1147  const std::string line = VGETTEXT("Remaining: $left/$max",
1148  {{"left", std::to_string(left)},
1149  {"max", std::to_string(max)}});
1150  add_text(res, " " + span_color(font::weapon_details_color, line) + "\n",
1151  _("This unit can attack multiple times per turn."));
1152  }
1153 
1154  for (const attack_type &at : u->attacks())
1155  {
1156  attack_info(rc, at, res, *u, hex);
1157  }
1158  }
1159  return res;
1160 }
1162 {
1163  const unit *u = get_visible_unit(rc);
1164  const map_location& mouseover_hex = rc.screen().mouseover_hex();
1165  const map_location& displayed_unit_hex = rc.screen().displayed_unit_hex();
1166  const map_location& hex = mouseover_hex.valid() ? mouseover_hex : displayed_unit_hex;
1167  if (!u) return config();
1168 
1169  return unit_weapons(rc, u, hex);
1170 }
1171 REPORT_GENERATOR(highlighted_unit_weapons, rc)
1172 {
1174  const unit *sec_u = get_visible_unit(rc);
1175 
1176  if (!u) return report_unit_weapons(rc);
1177  if (!sec_u || u.get() == sec_u) return unit_weapons(rc, sec_u, rc.screen().mouseover_hex());
1178 
1179  map_location highlighted_hex = rc.screen().displayed_unit_hex();
1180  map_location attack_loc;
1181  if (rc.mhb())
1182  attack_loc = rc.mhb()->current_unit_attacks_from(highlighted_hex);
1183 
1184  if (!attack_loc.valid())
1185  return unit_weapons(rc, sec_u, rc.screen().mouseover_hex());
1186 
1187  //TODO: shouldn't this pass sec_u as secodn parameter ?
1188  return unit_weapons(rc, u, attack_loc, sec_u, false);
1189 }
1190 REPORT_GENERATOR(selected_unit_weapons, rc)
1191 {
1193  const unit *sec_u = get_visible_unit(rc);
1194 
1195  if (!u) return config();
1196  if (!sec_u || u.get() == sec_u) return unit_weapons(rc, u.get(), u->get_location());
1197 
1198  map_location highlighted_hex = rc.screen().displayed_unit_hex();
1199  map_location attack_loc;
1200  if (rc.mhb())
1201  attack_loc = rc.mhb()->current_unit_attacks_from(highlighted_hex);
1202 
1203  if (!attack_loc.valid())
1204  return unit_weapons(rc, u.get(), u->get_location());
1205 
1206  return unit_weapons(rc, u, attack_loc, sec_u, true);
1207 }
1208 
1209 REPORT_GENERATOR(unit_image,rc)
1210 {
1211  const unit *u = get_visible_unit(rc);
1212  if (!u) return config();
1213  return image_report(u->absolute_image() + u->image_mods());
1214 }
1215 REPORT_GENERATOR(selected_unit_image, rc)
1216 {
1217  const unit *u = get_selected_unit(rc);
1218  if (!u) return config();
1219  return image_report(u->absolute_image() + u->image_mods());
1220 }
1221 
1222 REPORT_GENERATOR(selected_unit_profile, rc)
1223 {
1224  const unit *u = get_selected_unit(rc);
1225  if (!u) return config();
1226  return image_report(u->small_profile());
1227 }
1228 REPORT_GENERATOR(unit_profile, rc)
1229 {
1230  const unit *u = get_visible_unit(rc);
1231  if (!u) return config();
1232  return image_report(u->small_profile());
1233 }
1234 
1236 {
1237  std::ostringstream tooltip;
1238  std::ostringstream text;
1239 
1240  const map_location& tod_schedule_hex = (hex.valid() && !display::get_singleton()->shrouded(hex)) ? hex : map_location::null_location();
1241  const std::vector<time_of_day>& schedule = rc.tod().times(tod_schedule_hex);
1242 
1243  tooltip << _("Time of day schedule:") << " \n";
1244  int current = rc.tod().get_current_time(tod_schedule_hex);
1245  int i = 0;
1246  for (const time_of_day& tod : schedule) {
1247  if (i == current) tooltip << "<big><b>";
1248  tooltip << tod.name << "\n";
1249  if (i == current) tooltip << "</b></big>";
1250  i++;
1251  }
1252 
1253  int times = schedule.size();
1254  text << current + 1 << "/" << times;
1255 
1256  return text_report(text.str(), tooltip.str(), "..schedule");
1257 }
1258 REPORT_GENERATOR(tod_stats, rc)
1259 {
1260  map_location mouseover_hex = rc.screen().mouseover_hex();
1261  if (mouseover_hex.valid()) return tod_stats_at(rc, mouseover_hex);
1262  return tod_stats_at(rc, rc.screen().selected_hex());
1263 }
1264 REPORT_GENERATOR(selected_tod_stats, rc)
1265 {
1266  const unit *u = get_selected_unit(rc);
1267  if(!u) return tod_stats_at(rc, map_location::null_location());
1268  const map_location& attack_indicator_src = game_display::get_singleton()->get_attack_indicator_src();
1269  const map_location& hex =
1270  attack_indicator_src.valid() ? attack_indicator_src : u->get_location();
1271  return tod_stats_at(rc, hex);
1272 }
1273 
1274 static config time_of_day_at(reports::context & rc, const map_location& mouseover_hex)
1275 {
1276  std::ostringstream tooltip;
1277  time_of_day tod = get_visible_time_of_day_at(rc, mouseover_hex);
1278 
1279  int b = tod.lawful_bonus;
1280  int l = generic_combat_modifier(b, unit_alignments::type::liminal, false, rc.tod().get_max_liminal_bonus());
1281  std::string lawful_color("white");
1282  std::string chaotic_color("white");
1283  std::string liminal_color("white");
1284 
1285  if (b != 0) {
1286  lawful_color = (b > 0) ? "#0f0" : "#f00";
1287  chaotic_color = (b < 0) ? "#0f0" : "#f00";
1288  }
1289  if (l != 0) {
1290  liminal_color = (l > 0) ? "#0f0" : "#f00";
1291  }
1292  tooltip << _("Time of day:") << " <b>" << tod.name << "</b>\n"
1293  << _("Lawful units: ") << "<span foreground=\"" << lawful_color << "\">"
1294  << utils::signed_percent(b) << "</span>\n"
1295  << _("Neutral units: ") << utils::signed_percent(0) << '\n'
1296  << _("Chaotic units: ") << "<span foreground=\"" << chaotic_color << "\">"
1297  << utils::signed_percent(-b) << "</span>\n"
1298  << _("Liminal units: ") << "<span foreground=\"" << liminal_color << "\">"
1299  << utils::signed_percent(l) << "</span>\n";
1300 
1301  std::string tod_image = tod.image;
1302  if(tod.bonus_modified > 0) {
1303  tod_image += (formatter() << "~BLIT(" << game_config::images::tod_bright << ")").str();
1304  } else if(tod.bonus_modified < 0) {
1305  tod_image += (formatter() << "~BLIT(" << game_config::images::tod_dark << ")").str();
1306  }
1307 
1308  return image_report(tod_image, tooltip.str(), "time_of_day_" + tod.id);
1309 }
1311 {
1312  map_location mouseover_hex = rc.screen().mouseover_hex();
1313  if (mouseover_hex.valid()) return time_of_day_at(rc, mouseover_hex);
1314  return time_of_day_at(rc, rc.screen().selected_hex());
1315 }
1316 REPORT_GENERATOR(selected_time_of_day, rc)
1317 {
1318  const unit *u = get_selected_unit(rc);
1319  if(!u) return time_of_day_at(rc, map_location::null_location());
1320  const map_location& attack_indicator_src = game_display::get_singleton()->get_attack_indicator_src();
1321  const map_location& hex =
1322  attack_indicator_src.valid() ? attack_indicator_src : u->get_location();
1323  return time_of_day_at(rc, hex);
1324 }
1325 
1326 static config unit_box_at(reports::context & rc, const map_location& mouseover_hex)
1327 {
1328  std::ostringstream tooltip;
1329  time_of_day global_tod = rc.tod().get_time_of_day();
1330  time_of_day local_tod = get_visible_time_of_day_at(rc, mouseover_hex);
1331 
1332  int bonus = local_tod.lawful_bonus;
1333  int l = generic_combat_modifier(bonus, unit_alignments::type::liminal, false, rc.tod().get_max_liminal_bonus());
1334 
1335  std::string lawful_color("white");
1336  std::string chaotic_color("white");
1337  std::string liminal_color("white");
1338 
1339  if (bonus != 0) {
1340  lawful_color = (bonus > 0) ? "green" : "red";
1341  chaotic_color = (bonus < 0) ? "green" : "red";
1342  }
1343  if (l != 0) {
1344  liminal_color = (l > 0) ? "green" : "red";
1345  }
1346  tooltip << local_tod.name << '\n'
1347  << _("Lawful units: ") << "<span foreground=\"" << lawful_color << "\">"
1348  << utils::signed_percent(bonus) << "</span>\n"
1349  << _("Neutral units: ") << utils::signed_percent(0) << '\n'
1350  << _("Chaotic units: ") << "<span foreground=\"" << chaotic_color << "\">"
1351  << utils::signed_percent(-bonus) << "</span>\n"
1352  << _("Liminal units: ") << "<span foreground=\"" << liminal_color << "\">"
1353  << utils::signed_percent(l) << "</span>\n";
1354 
1355  std::string local_tod_image = "themes/classic/" + local_tod.image;
1356  std::string global_tod_image = "themes/classic/" + global_tod.image;
1357  if(local_tod.bonus_modified != 0) {
1358  local_tod_image += "~BLIT(";
1359  if (local_tod.bonus_modified > 0) local_tod_image += game_config::images::tod_bright;
1360  else if (local_tod.bonus_modified < 0) local_tod_image += game_config::images::tod_dark;
1361  local_tod_image += ")";
1362  }
1363 
1364  const gamemap &map = rc.map();
1365  t_translation::terrain_code terrain = map.get_terrain(mouseover_hex);
1366 
1367  //if (t_translation::terrain_matches(terrain, t_translation::ALL_OFF_MAP))
1368  // return config();
1369 
1370  //if (map.is_keep(mouseover_hex)) {
1371  // add_image(cfg, "icons/terrain/terrain_type_keep.png", "");
1372  //}
1373 
1374  const t_translation::ter_list& underlying_terrains = map.underlying_union_terrain(terrain);
1375 
1376  std::string bg_terrain_image;
1377 
1378  for (const t_translation::terrain_code& underlying_terrain : underlying_terrains) {
1379  const std::string& terrain_id = map.get_terrain_info(underlying_terrain).id();
1380  bg_terrain_image = "~BLIT(unit_env/terrain/terrain-" + terrain_id + ".png)" + bg_terrain_image;
1381  }
1382 
1383  std::stringstream color;
1384  color << local_tod.color;
1385 
1386  bg_terrain_image = bg_terrain_image + "~CS(" + color.str() + ")";
1387 
1388  const unit *u = get_visible_unit(rc);
1389  std::string unit_image;
1390  if (u)
1391  unit_image = "~BLIT(" + u->absolute_image() + u->image_mods() + ",35,22)";
1392 
1393  std::string tod_image = global_tod_image + "~BLIT(" + local_tod_image + ")";
1394 
1395  return image_report(tod_image + bg_terrain_image + unit_image, tooltip.str(), "time_of_day");
1396 }
1397 REPORT_GENERATOR(unit_box, rc)
1398 {
1399  map_location mouseover_hex = rc.screen().mouseover_hex();
1400  return unit_box_at(rc, mouseover_hex);
1401 }
1402 
1403 
1405 {
1406  std::ostringstream str, tooltip;
1407  str << rc.tod().turn();
1408  int nb = rc.tod().number_of_turns();
1409  if (nb != -1) str << '/' << nb;
1410 
1411  tooltip << _("Turn Number");
1412  if(nb != -1) {
1413  tooltip << "\n\n" << _("When the game exceeds the number of turns indicated by the second number, it will end.");
1414  }
1415  return text_report(str.str(), tooltip.str());
1416 }
1417 
1419 {
1420  std::ostringstream str;
1421  int viewing_side = rc.screen().viewing_side();
1422  // Suppose the full unit map is applied.
1423  int fake_gold = rc.dc().get_team(viewing_side).gold();
1424 
1425  if (rc.wb())
1426  fake_gold -= rc.wb()->get_spent_gold_for(viewing_side);
1427  char const *end = naps;
1428  if (viewing_side != rc.screen().playing_side()) {
1429  str << span_color(font::GRAY_COLOR);
1430  }
1431  else if (fake_gold < 0) {
1432  str << span_color(font::BAD_COLOR);
1433  }
1434  else {
1435  end = "";
1436  }
1437  str << utils::half_signed_value(fake_gold) << end;
1438  return text_report(str.str(), _("Gold") + "\n\n" + _("The amount of gold currently available to recruit and maintain your army."));
1439 }
1440 
1441 REPORT_GENERATOR(villages, rc)
1442 {
1443  std::ostringstream str;
1444  int viewing_side = rc.screen().viewing_side();
1445  const team &viewing_team = rc.dc().get_team(viewing_side);
1446  str << viewing_team.villages().size() << '/';
1447  if (viewing_team.uses_shroud()) {
1448  int unshrouded_villages = 0;
1449  for (const map_location &loc : rc.map().villages()) {
1450  if (!viewing_team.shrouded(loc))
1451  ++unshrouded_villages;
1452  }
1453  str << unshrouded_villages;
1454  } else {
1455  str << rc.map().villages().size();
1456  }
1457  return gray_inactive(rc,str.str(), _("Villages") + "\n\n" + _("The fraction of known villages that your side has captured."));
1458 }
1459 
1460 REPORT_GENERATOR(num_units, rc)
1461 {
1462  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."));
1463 }
1464 
1465 REPORT_GENERATOR(upkeep, rc)
1466 {
1467  std::ostringstream str;
1468  int viewing_side = rc.screen().viewing_side();
1469  const team &viewing_team = rc.dc().get_team(viewing_side);
1470  team_data td(rc.dc(), viewing_team);
1471  str << td.expenses << " (" << td.upkeep << ")";
1472  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."));
1473 }
1474 
1475 REPORT_GENERATOR(expenses, rc)
1476 {
1477  int viewing_side = rc.screen().viewing_side();
1478  const team &viewing_team = rc.dc().get_team(viewing_side);
1479  team_data td(rc.dc(), viewing_team);
1480  return gray_inactive(rc,std::to_string(td.expenses));
1481 }
1482 
1483 REPORT_GENERATOR(income, rc)
1484 {
1485  std::ostringstream str;
1486  int viewing_side = rc.screen().viewing_side();
1487  const team &viewing_team = rc.dc().get_team(viewing_side);
1488  team_data td(rc.dc(), viewing_team);
1489  char const *end = naps;
1490  if (viewing_side != rc.screen().playing_side()) {
1491  if (td.net_income < 0) {
1492  td.net_income = - td.net_income;
1493  str << span_color(font::GRAY_COLOR);
1494  str << font::unicode_minus;
1495  }
1496  else {
1497  str << span_color(font::GRAY_COLOR);
1498  }
1499  }
1500  else if (td.net_income < 0) {
1501  td.net_income = - td.net_income;
1502  str << span_color(font::BAD_COLOR);
1503  str << font::unicode_minus;
1504  }
1505  else {
1506  end = "";
1507  }
1508  str << td.net_income << end;
1509  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."));
1510 }
1511 
1512 namespace {
1513 void blit_tced_icon(config &cfg, const std::string &terrain_id, const std::string &icon_image, bool high_res,
1514  const std::string &terrain_name) {
1515  const std::string tc_base = high_res ? "images/buttons/icon-base-32.png" : "images/buttons/icon-base-16.png";
1516  const std::string terrain_image = "terrain/" + icon_image + (high_res ? "_30.png" : ".png");
1517  add_image(cfg, tc_base + "~RC(magenta>" + terrain_id + ")~BLIT(" + terrain_image + ")", terrain_name);
1518 }
1519 }
1520 
1521 REPORT_GENERATOR(terrain_info, rc)
1522 {
1523  const gamemap &map = rc.map();
1524  map_location mouseover_hex = rc.screen().mouseover_hex();
1525 
1526  if (!map.on_board(mouseover_hex))
1527  mouseover_hex = rc.screen().selected_hex();
1528 
1529  if (!map.on_board(mouseover_hex))
1530  return config();
1531 
1532  t_translation::terrain_code terrain = map.get_terrain(mouseover_hex);
1534  return config();
1535 
1536  config cfg;
1537 
1538  bool high_res = false;
1539 
1540  if (display::get_singleton()->shrouded(mouseover_hex)) {
1541  return cfg;
1542  }
1543  //TODO
1544 // if (display::get_singleton()->fogged(mouseover_hex)) {
1545 // blit_tced_icon(cfg, "fog", high_res);
1546 // }
1547 //
1548 // if (map.is_keep(mouseover_hex)) {
1549 // blit_tced_icon(cfg, "keep", high_res);
1550 // }
1551 
1552  const t_translation::ter_list& underlying_terrains = map.underlying_union_terrain(terrain);
1553  for (const t_translation::terrain_code& underlying_terrain : underlying_terrains) {
1554 
1556  continue;
1557  const std::string& terrain_id = map.get_terrain_info(underlying_terrain).id();
1558  const std::string& terrain_name = map.get_terrain_string(underlying_terrain);
1559  const std::string& terrain_icon = map.get_terrain_info(underlying_terrain).icon_image();
1560  if (terrain_icon.empty())
1561  continue;
1562  blit_tced_icon(cfg, terrain_id, terrain_icon, high_res, terrain_name);
1563  }
1564  return cfg;
1565 }
1566 
1567 REPORT_GENERATOR(terrain, rc)
1568 {
1569  const gamemap &map = rc.map();
1570  int viewing_side = rc.screen().viewing_side();
1571  const team &viewing_team = rc.dc().get_team(viewing_side);
1572  map_location mouseover_hex = rc.screen().mouseover_hex();
1573  if (!map.on_board(mouseover_hex) || viewing_team.shrouded(mouseover_hex))
1574  return config();
1575 
1576  t_translation::terrain_code terrain = map.get_terrain(mouseover_hex);
1578  return config();
1579 
1580  std::ostringstream str;
1581  if (map.is_village(mouseover_hex))
1582  {
1583  int owner = rc.dc().village_owner(mouseover_hex);
1584  if (owner == 0 || viewing_team.fogged(mouseover_hex)) {
1585  str << map.get_terrain_info(terrain).income_description();
1586  } else if (owner == viewing_side) {
1587  str << map.get_terrain_info(terrain).income_description_own();
1588  } else if (viewing_team.is_enemy(owner)) {
1589  str << map.get_terrain_info(terrain).income_description_enemy();
1590  } else {
1591  str << map.get_terrain_info(terrain).income_description_ally();
1592  }
1593 
1594  const std::string& underlying_desc = map.get_underlying_terrain_string(terrain);
1595  if(!underlying_desc.empty()) {
1596  str << underlying_desc;
1597  }
1598  } else {
1599  str << map.get_terrain_string(terrain);
1600  }
1601 
1602  return text_report(str.str());
1603 }
1604 
1605 REPORT_GENERATOR(zoom_level, rc)
1606 {
1607  std::ostringstream text;
1608  std::ostringstream tooltip;
1609  std::ostringstream help;
1610 
1611  text << static_cast<int>(rc.screen().get_zoom_factor() * 100) << "%";
1612 
1613  return text_report(text.str(), tooltip.str(), help.str());
1614 }
1615 
1616 REPORT_GENERATOR(position, rc)
1617 {
1618  const gamemap &map = rc.map();
1619  map_location mouseover_hex = rc.screen().mouseover_hex(),
1620  displayed_unit_hex = rc.screen().displayed_unit_hex(),
1621  selected_hex = rc.screen().selected_hex();
1622 
1623  if (!map.on_board(mouseover_hex)) {
1624  if (!map.on_board(selected_hex))
1625  return config();
1626  else {
1627  mouseover_hex = selected_hex;
1628  }
1629  }
1630 
1631  t_translation::terrain_code terrain = map[mouseover_hex];
1633  return config();
1634 
1635  std::ostringstream str;
1636  str << mouseover_hex;
1637 
1638  const unit *u = get_visible_unit(rc);
1639  const team &viewing_team = rc.teams()[rc.screen().viewing_team()];
1640  if (!u ||
1641  (displayed_unit_hex != mouseover_hex &&
1642  displayed_unit_hex != rc.screen().selected_hex()) ||
1643  viewing_team.shrouded(mouseover_hex))
1644  return text_report(str.str());
1645 
1646  int move_cost = u->movement_cost(terrain);
1647  int defense = 100 - u->defense_modifier(terrain);
1648  if (move_cost < movetype::UNREACHABLE) {
1649  str << " " << defense << "%," << move_cost;
1650  } else if (mouseover_hex == displayed_unit_hex) {
1651  str << " " << defense << "%,‒";
1652  } else {
1653  str << " ‒";
1654  }
1655  return text_report(str.str());
1656 }
1657 
1658 REPORT_GENERATOR(side_playing, rc)
1659 {
1660  const team &active_team = rc.teams()[rc.screen().playing_team()];
1661  std::string flag_icon = active_team.flag_icon();
1662  std::string old_rgb = game_config::flag_rgb;
1663  std::string new_rgb = team::get_side_color_id(rc.screen().playing_side());
1664  std::string mods = "~RC(" + old_rgb + ">" + new_rgb + ")";
1665  if (flag_icon.empty())
1666  flag_icon = game_config::images::flag_icon;
1667  return image_report(flag_icon + mods, side_tooltip(active_team));
1668 }
1669 
1670 REPORT_GENERATOR(observers, rc)
1671 {
1672  const std::set<std::string> &observers = rc.screen().observers();
1673  if (observers.empty())
1674  return config();
1675 
1676  std::ostringstream str;
1677  str << _("Observers:") << '\n';
1678  for (const std::string &obs : observers) {
1679  str << obs << '\n';
1680  }
1681  return image_report(game_config::images::observer, str.str());
1682 }
1683 
1684 /* TODO unused
1685 REPORT_GENERATOR(selected_terrain)
1686 {
1687  const std::string selected_terrain = editor::get_selected_terrain();
1688  if (selected_terrain.empty())
1689  return config();
1690  else
1691  return text_report(selected_terrain);
1692 }
1693 */
1694 
1695 /* TODO this is unused
1696 REPORT_GENERATOR(edit_left_button_function)
1697 {
1698  const std::string left_button_function = editor::get_left_button_function();
1699  if (left_button_function.empty())
1700  return config();
1701  else
1702  return text_report(left_button_function);
1703 }
1704 */
1705 
1706 REPORT_GENERATOR(report_clock, /*rc*/)
1707 {
1708  config report;
1710 
1711  std::ostringstream ss;
1712 
1714  ? "%I:%M %p"
1715  : "%H:%M";
1716 
1717  std::time_t t = std::time(nullptr);
1718  ss << std::put_time(std::localtime(&t), format);
1719  add_text(report, ss.str(), _("Clock"));
1720 
1721  return report;
1722 }
1723 
1724 
1725 REPORT_GENERATOR(battery, /*rc*/)
1726 {
1727  config report;
1728 
1730  add_text(report, (boost::format("%.0f %%") % desktop::battery_info::get_battery_percentage()).str(), _("Battery"));
1731 
1732  return report;
1733 }
1734 
1735 REPORT_GENERATOR(report_countdown, rc)
1736 {
1737  int viewing_side = rc.screen().viewing_side();
1738  const team &viewing_team = rc.dc().get_team(viewing_side);
1739  int min, sec;
1740  if (viewing_team.countdown_time() == 0)
1741  return report_report_clock(rc);
1742  std::ostringstream str;
1743  sec = viewing_team.countdown_time() / 1000;
1744  char const *end = naps;
1745  if (viewing_side != rc.screen().playing_side())
1746  str << span_color(font::GRAY_COLOR);
1747  else if (sec < 60)
1748  str << "<span foreground=\"#c80000\">";
1749  else if (sec < 120)
1750  str << "<span foreground=\"#c8c800\">";
1751  else
1752  end = "";
1753  min = sec / 60;
1754  str << min << ':';
1755  sec = sec % 60;
1756  if (sec < 10) str << '0';
1757  str << sec << end;
1758 
1759  config report;
1761  add_text(report, str.str(), _("Turn Countdown") + "\n\n" + _("Countdown until your turn automatically ends."));
1762 
1763  return report;
1764 }
1765 
1766 void reports::register_generator(const std::string &name, reports::generator *g)
1767 {
1768  dynamic_generators_[name].reset(g);
1769 }
1770 
1771 config reports::generate_report(const std::string &name, reports::context & rc, bool only_static)
1772 {
1773  if (!only_static) {
1774  dynamic_report_generators::const_iterator i = dynamic_generators_.find(name);
1775  if (i != dynamic_generators_.end())
1776  return i->second->generate(rc);
1777  }
1778  static_report_generators::const_iterator j = static_generators.find(name);
1779  if (j != static_generators.end())
1780  return j->second(rc);
1781  return config();
1782 }
1783 
1784 const std::set<std::string> &reports::report_list()
1785 {
1786  if (!all_reports_.empty()) return all_reports_;
1787  for (const static_report_generators::value_type &v : static_generators) {
1788  all_reports_.insert(v.first);
1789  }
1790  for (const dynamic_report_generators::value_type &v : dynamic_generators_) {
1791  all_reports_.insert(v.first);
1792  }
1793  return all_reports_;
1794 }
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:982
static config unit_xp(const unit *u)
Definition: reports.cpp:517
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:97
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:206
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:1394
int vision() const
Gets the unit&#39;s vision points.
Definition: unit.hpp:1388
const std::string weapon_details_sep
Definition: constants.cpp:50
This class represents a single unit of a specific type.
Definition: unit.hpp:120
const std::string & type_id() const
The id of this unit&#39;s type.
Definition: unit.cpp:1804
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:1274
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:1428
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:544
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:288
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:1314
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:455
const map_location & get_attack_indicator_src()
logger & info()
Definition: log.cpp:171
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:489
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:1623
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:763
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:1596
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:99
const std::set< std::string > & report_list()
Definition: reports.cpp:1784
static std::string format_hp(unsigned hp)
Definition: reports.cpp:1004
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
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:1100
The unit is slowed - it moves slower and does less damage.
Definition: unit.hpp:851
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:112
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:992
The unit is poisoned - it loses health each turn.
Definition: unit.hpp:852
std::string absolute_image() const
The name of the file to game_display (used in menus).
Definition: unit.cpp:2416
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:757
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:1235
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:76
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:327
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:345
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:1085
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:1011
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:1608
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:77
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:370
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:1766
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:1139
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:1771
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:393
const terrain_code MINUS
The basic class for representing 8-bit RGB or RGBA colour values.
Definition: color.hpp:62
Computes the statistics of a battle between an attacker and a defender unit.
Definition: attack.hpp:173
const terrain_code PLUS
utils::string_map_res get_base_resistances() const
Gets resistances without any abilities applied.
Definition: unit.hpp:1037
int max_experience() const
The max number of experience points this unit can have.
Definition: unit.hpp:519
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:807
virtual int playing_side() const
Definition: display.hpp:207
std::string path
Definition: game_config.cpp:39
int level() const
The current level of this unit.
Definition: unit.hpp:549
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:1071
const std::string & icon() const
Definition: attack_type.hpp:46
static config unit_hp(reports::context &rc, const unit *u)
Definition: reports.cpp:464
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:359
std::string small_profile() const
An optional profile image to display in Help.
Definition: unit.cpp:1024
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:465
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:730
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:1326
bool invisible(const map_location &loc, bool see_all=true) const
Definition: unit.cpp:2439
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:495
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:98
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:915
std::map< std::string, std::string > advancement_icons() const
Gets and image path and and associated description for each advancement option.
Definition: unit.cpp:1676
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()
int modified_damage(bool is_backstab) const
Returns the damage per attack of this weapon, considering specials.
Definition: abilities.cpp:1127
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:859
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:483
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:564
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:1235
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:1603
int experience() const
The current number of experience points this unit has.
Definition: unit.hpp:513
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1345
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:111
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:262
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:658
t_string unit_description() const
A detailed description of this unit.
Definition: unit.hpp:440
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:1254
unsigned int num_blows
Effective number of blows, takes swarm into account.
Definition: attack.hpp:81
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:333
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:1651
std::vector< std::pair< std::string, std::string > > amla_icons() const
Gets the image and description data for modification advancements.
Definition: unit.cpp:1716
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:630
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:967
int movement_left() const
Gets how far a unit can move, considering the incapacitated flag.
Definition: unit.hpp:1270
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:966
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:1081
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