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