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