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