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 "whiteboard/manager.hpp"
38 
39 #include <cassert>
40 #include <ctime>
41 #include <iomanip>
42 #include <boost/dynamic_bitset.hpp>
43 #include <boost/format.hpp>
44 #include <boost/lexical_cast.hpp>
45 
46 static void add_text(config &report, const std::string &text,
47  const std::string &tooltip, const std::string &help = "")
48 {
49  config &element = report.add_child("element");
50  element["text"] = text;
51  if (!tooltip.empty()) element["tooltip"] = tooltip;
52  if (!help.empty()) element["help"] = help;
53 }
54 
55 static void add_image(config &report, const std::string &image,
56  const std::string &tooltip, const std::string &help = "")
57 {
58  config &element = report.add_child("element");
59  element["image"] = image;
60  if (!tooltip.empty()) element["tooltip"] = tooltip;
61  if (!help.empty()) element["help"] = help;
62 }
63 
64 static config text_report(const std::string &text,
65  const std::string &tooltip = "", const std::string &help = "")
66 {
67  config r;
68  add_text(r, text, tooltip, help);
69  return r;
70 }
71 
72 static config image_report(const std::string &image,
73  const std::string &tooltip = "", const std::string &help = "")
74 {
75  config r;
76  add_image(r, image, tooltip, help);
77  return r;
78 }
79 
80 using font::span_color;
81 
82 static void add_status(config &r,
83  char const *path, char const *desc1, char const *desc2)
84 {
85  std::ostringstream s;
86  s << translation::gettext(desc1) << translation::gettext(desc2);
87  add_image(r, path, s.str());
88 }
89 
90 static std::string flush(std::ostringstream &s)
91 {
92  std::string r(s.str());
93  s.str(std::string());
94  return r;
95 }
96 
98 {
99  const team &viewing_team = rc.teams()[rc.screen().viewing_team()];
100  if (viewing_team.shrouded(hex)) {
101  // Don't show time on shrouded tiles.
102  return rc.tod().get_time_of_day();
103  } else if (viewing_team.fogged(hex)) {
104  // Don't show illuminated time on fogged tiles.
105  return rc.tod().get_time_of_day(hex);
106  } else {
107  return rc.tod().get_illuminated_time_of_day(rc.units(), rc.map(), hex);
108  }
109 }
110 
111 typedef std::map<std::string, reports::generator_function> static_report_generators;
113 
115 {
117  {
118  static_generators.insert(static_report_generators::value_type(name, g));
119  }
120 };
121 
122 #define REPORT_GENERATOR(n, cn) \
123  static config report_##n(reports::context & cn); \
124  static report_generator_helper reg_gen_##n(#n, &report_##n); \
125  static config report_##n(reports::context & cn)
126 
127 static char const *naps = "</span>";
128 
130 {
131  return rc.dc().get_visible_unit(rc.screen().displayed_unit_hex(),
132  rc.teams()[rc.screen().viewing_team()],
133  rc.screen().show_everything());
134 }
135 
137 {
138  return rc.dc().get_visible_unit(rc.screen().selected_hex(),
139  rc.teams()[rc.screen().viewing_team()],
140  rc.screen().show_everything());
141 }
142 
143 static config gray_inactive(reports::context & rc, const std::string &str, const std::string& tooltip = "")
144 {
145  if ( rc.screen().viewing_side() == rc.screen().playing_side() )
146  return text_report(str, tooltip);
147 
148  return text_report(span_color(font::GRAY_COLOR) + str + naps, tooltip);
149 }
150 
151 static config unit_name(const unit *u)
152 {
153  if (!u) {
154  return config();
155  }
156 
157  /*
158  * The name needs to be escaped, it might be set by the user and using
159  * markup. Also names often contain a forbidden single quote.
160  */
161  const std::string& name = font::escape_text(u->name());
162  std::ostringstream str, tooltip;
163  str << "<b>" << name << "</b>";
164  tooltip << _("Name: ") << "<b>" << name << "</b>";
165  return text_report(str.str(), tooltip.str());
166 }
167 
169 {
170  const unit *u = get_visible_unit(rc);
171  return unit_name(u);
172 }
173 REPORT_GENERATOR(selected_unit_name, rc)
174 {
175  const unit *u = get_selected_unit(rc);
176  return unit_name(u);
177 }
178 
179 static config unit_type(const unit* u)
180 {
181  if (!u) return config();
182  std::string has_variations_prefix = (u->type().show_variations_in_help() ? ".." : "");
183  std::ostringstream str, tooltip;
184  str << u->type_name();
185  tooltip << _("Type: ") << "<b>" << u->type_name() << "</b>\n"
186  << u->unit_description();
187  return text_report(str.str(), tooltip.str(), has_variations_prefix + "unit_" + u->type_id());
188 }
190 {
191  const unit *u = get_visible_unit(rc);
192  return unit_type(u);
193 }
194 REPORT_GENERATOR(selected_unit_type, rc)
195 {
196  const unit *u = get_selected_unit(rc);
197  return unit_type(u);
198 }
199 
200 static config unit_race(const unit* u)
201 {
202  if (!u) return config();
203  std::ostringstream str, tooltip;
204  str << u->race()->name(u->gender());
205  tooltip << _("Race: ") << "<b>" << u->race()->name(u->gender()) << "</b>";
206  return text_report(str.str(), tooltip.str(), "..race_" + u->race()->id());
207 }
209 {
210  const unit *u = get_visible_unit(rc);
211  return unit_race(u);
212 }
213 REPORT_GENERATOR(selected_unit_race, rc)
214 {
215  const unit *u = get_selected_unit(rc);
216  return unit_race(u);
217 }
218 
219 static std::string side_tooltip(const team& team)
220 {
221  if(team.side_name().empty())
222  return "";
223 
224  return VGETTEXT("Side: <b>$side_name</b> ($color_name)",
225  {{"side_name", team.side_name()},
226  {"color_name", team::get_side_color_name_for_UI(team.side()) }});
227 }
228 
229 static config unit_side(reports::context & rc, const unit* u)
230 {
231  if (!u) return config();
232 
233  config report;
234  const team &u_team = rc.dc().get_team(u->side());
235  std::string flag_icon = u_team.flag_icon();
236  std::string old_rgb = game_config::flag_rgb;
237  std::string new_rgb = u_team.color();
238  std::string mods = "~RC(" + old_rgb + ">" + new_rgb + ")";
239  if (flag_icon.empty())
240  flag_icon = game_config::images::flag_icon;
241 
242  std::stringstream text;
243  text << " " << u->side();
244 
245  const std::string& tooltip = side_tooltip(u_team);
246  add_image(report, flag_icon + mods, tooltip, "");
247  add_text(report, text.str(), tooltip, "");
248  return report;
249 }
251 {
252  const unit *u = get_visible_unit(rc);
253  return unit_side(rc,u);
254 }
255 REPORT_GENERATOR(selected_unit_side, rc)
256 {
257  const unit *u = get_selected_unit(rc);
258  return unit_side(rc, u);
259 }
260 
261 static config unit_level(const unit* u)
262 {
263  if (!u) return config();
264  return text_report(std::to_string(u->level()), unit_helper::unit_level_tooltip(*u));
265 }
267 {
268  const unit *u = get_visible_unit(rc);
269  return unit_level(u);
270 }
271 REPORT_GENERATOR(selected_unit_level, rc)
272 {
273  const unit *u = get_selected_unit(rc);
274  return unit_level(u);
275 }
276 
277 REPORT_GENERATOR(unit_amla, rc)
278 {
279  const unit *u = get_visible_unit(rc);
280  if (!u) return config();
281  config res;
282  typedef std::pair<std::string, std::string> pair_string;
283  for (const pair_string &ps : u->amla_icons()) {
284  add_image(res, ps.first, ps.second);
285  }
286  return res;
287 }
288 
289 static config unit_traits(const unit* u)
290 {
291  if (!u) return config();
292  config res;
293  const std::vector<t_string> &traits = u->trait_names();
294  const std::vector<t_string> &descriptions = u->trait_descriptions();
295  const std::vector<std::string> &trait_ids = u->get_traits_list();
296  unsigned nb = traits.size();
297  for (unsigned i = 0; i < nb; ++i)
298  {
299  std::ostringstream str, tooltip;
300  str << traits[i];
301  if (i != nb - 1 ) str << ", ";
302  tooltip << _("Trait: ") << "<b>" << traits[i] << "</b>\n"
303  << descriptions[i];
304  add_text(res, str.str(), tooltip.str(), "traits_" + trait_ids[i]);
305  }
306  return res;
307 }
309 {
310  const unit *u = get_visible_unit(rc);
311  return unit_traits(u);
312 }
313 REPORT_GENERATOR(selected_unit_traits, rc)
314 {
315  const unit *u = get_selected_unit(rc);
316  return unit_traits(u);
317 }
318 
319 static config unit_status(reports::context & rc, const unit* u)
320 {
321  if (!u) return config();
322  config res;
323  map_location displayed_unit_hex = rc.screen().displayed_unit_hex();
324  if (rc.map().on_board(displayed_unit_hex) && u->invisible(displayed_unit_hex)) {
325  add_status(res, "misc/invisible.png", N_("invisible: "),
326  N_("This unit is invisible. It cannot be seen or attacked by enemy units."));
327  }
328  if (u->get_state(unit::STATE_SLOWED)) {
329  add_status(res, "misc/slowed.png", N_("slowed: "),
330  N_("This unit has been slowed. It will only deal half its normal damage when attacking and its movement cost is doubled."));
331  }
333  add_status(res, "misc/poisoned.png", N_("poisoned: "),
334  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."));
335  }
337  add_status(res, "misc/petrified.png", N_("petrified: "),
338  N_("This unit has been petrified. It may not move or attack."));
339  }
340  return res;
341 }
343 {
344  const unit *u = get_visible_unit(rc);
345  return unit_status(rc,u);
346 }
347 REPORT_GENERATOR(selected_unit_status, rc)
348 {
349  const unit *u = get_selected_unit(rc);
350  return unit_status(rc, u);
351 }
352 
353 static config unit_alignment(reports::context & rc, const unit* u, const map_location& hex)
354 {
355  if (!u) return config();
356  std::ostringstream str, tooltip;
357  const std::string align = unit_type::alignment_description(u->alignment(), u->gender());
358  const std::string align_id = u->alignment().to_string();
359  const time_of_day effective_tod = get_visible_time_of_day_at(rc, hex);
360  int cm = combat_modifier(effective_tod, u->alignment(), u->is_fearless());
361 
362  color_t color = font::weapon_color;
363  if (cm != 0)
364  color = (cm > 0) ? font::good_dmg_color : font::bad_dmg_color;
365 
366  str << align << " (" << span_color(color) << utils::signed_percent(cm)
367  << naps << ")";
368 
369  tooltip << _("Alignment: ") << "<b>" << align << "</b>\n"
370  << string_table[align_id + "_description"];
371 
372  return text_report(str.str(), tooltip.str(), "time_of_day");
373 }
375 {
376  const unit *u = get_visible_unit(rc);
377  const map_location& mouseover_hex = rc.screen().mouseover_hex();
378  const map_location& displayed_unit_hex = rc.screen().displayed_unit_hex();
379  const map_location& hex = mouseover_hex.valid() ? mouseover_hex : displayed_unit_hex;
380  return unit_alignment(rc, u, hex);
381 }
382 REPORT_GENERATOR(selected_unit_alignment, rc)
383 {
384  const unit *u = get_selected_unit(rc);
385  const map_location& attack_indicator_src = game_display::get_singleton()->get_attack_indicator_src();
386  const map_location& hex_to_show_alignment_at =
387  attack_indicator_src.valid() ? attack_indicator_src : u->get_location();
388  return unit_alignment(rc, u, hex_to_show_alignment_at);
389 }
390 
391 static config unit_abilities(const unit* u, const map_location& loc)
392 {
393  if (!u) return config();
394  config res;
395 
396  boost::dynamic_bitset<> active;
397  const std::vector<std::tuple<std::string, t_string,t_string,t_string>> &abilities = u->ability_tooltips(active, loc);
398  const std::size_t abilities_size = abilities.size();
399  for ( std::size_t i = 0; i != abilities_size; ++i )
400  {
401  // Aliases for readability:
402  const std::string& id = std::get<0>(abilities[i]);
403  const std::string& base_name = std::get<1>(abilities[i]).base_str();
404  const t_string& display_name = std::get<2>(abilities[i]);
405  const t_string& description = std::get<3>(abilities[i]);
406 
407  std::ostringstream str, tooltip;
408 
409  if ( active[i] )
410  str << display_name;
411  else
412  str << span_color(font::inactive_ability_color) << display_name << naps;
413  if ( i + 1 != abilities_size )
414  str << ", ";
415 
416  tooltip << _("Ability: ") << "<b>" << display_name << "</b>";
417  if ( !active[i] )
418  tooltip << "<i>" << _(" (inactive)") << "</i>";
419  tooltip << '\n' << description;
420 
421  add_text(res, str.str(), tooltip.str(), "ability_" + id + base_name);
422  }
423  return res;
424 }
426 {
427  const unit *u = get_visible_unit(rc);
428  const map_location& mouseover_hex = rc.screen().mouseover_hex();
429 
430  return unit_abilities(u, mouseover_hex);
431 }
432 REPORT_GENERATOR(selected_unit_abilities, rc)
433 {
434  const unit *u = get_selected_unit(rc);
435 
436  const map_location& mouseover_hex = rc.screen().mouseover_hex();
437  const unit *visible_unit = get_visible_unit(rc);
438  if(visible_unit && u && visible_unit->id() != u->id() && mouseover_hex.valid())
439  return unit_abilities(u, mouseover_hex);
440  else
441  return unit_abilities(u, u->get_location());
442 }
443 
444 
445 static config unit_hp(reports::context& rc, const unit* u)
446 {
447  if (!u) return config();
448  std::ostringstream str, tooltip;
449  str << span_color(u->hp_color()) << u->hitpoints()
450  << '/' << u->max_hitpoints() << naps;
451 
452  std::set<std::string> resistances_table;
453 
454  bool att_def_diff = false;
455  map_location displayed_unit_hex = rc.screen().displayed_unit_hex();
456  for (const utils::string_map::value_type &resist : u->get_base_resistances())
457  {
458  std::ostringstream line;
459  line << translation::gettext(resist.first.c_str()) << ": ";
460  // Some units have different resistances when attacking or defending.
461  int res_att = 100 - u->resistance_against(resist.first, true, displayed_unit_hex);
462  int res_def = 100 - u->resistance_against(resist.first, false, displayed_unit_hex);
463  const std::string def_color = unit_helper::resistance_color(res_def);
464  if (res_att == res_def) {
465  line << "<span foreground=\"" << def_color << "\">" << utils::signed_percent(res_def)
466  << naps << '\n';
467  } else {
468  const std::string att_color = unit_helper::resistance_color(res_att);
469  line << "<span foreground=\"" << att_color << "\">" << utils::signed_percent(res_att)
470  << naps << "/"
471  << "<span foreground=\"" << def_color << "\">" << utils::signed_percent(res_def)
472  << naps << '\n';
473  att_def_diff = true;
474  }
475  resistances_table.insert(line.str());
476  }
477 
478  tooltip << _("Resistances: ");
479  if (att_def_diff)
480  tooltip << _("(Att / Def)");
481  tooltip << '\n';
482  for (const std::string &line : resistances_table) {
483  tooltip << line;
484  }
485  return text_report(str.str(), tooltip.str());
486 }
488 {
489  const unit *u = get_visible_unit(rc);
490  return unit_hp(rc, u);
491 }
492 REPORT_GENERATOR(selected_unit_hp, rc)
493 {
494  const unit *u = get_selected_unit(rc);
495  return unit_hp(rc, u);
496 }
497 
498 static config unit_xp(const unit* u)
499 {
500  if (!u) return config();
501  std::ostringstream str, tooltip;
502  str << span_color(u->xp_color());
503  if(u->can_advance()) {
504  str << u->experience() << '/' << u->max_experience();
505  } else {
506  str << font::unicode_en_dash;
507  }
508  str << naps;
509 
511  tooltip << _("Experience Modifier: ") << exp_mod << '%';
512  return text_report(str.str(), tooltip.str());
513 }
515 {
516  const unit *u = get_visible_unit(rc);
517  return unit_xp(u);
518 }
519 REPORT_GENERATOR(selected_unit_xp, rc)
520 {
521  const unit *u = get_selected_unit(rc);
522  return unit_xp(u);
523 }
524 
526 {
527  if (!u) return config();
528  config res;
529  typedef std::pair<std::string, std::string> pair_string;
530  for (const pair_string &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  typedef std::pair<int, std::set<std::string>> resist_units;
877  for (const resist_units &resist : resistances) {
878  int damage_with_resistance = round_damage(specials_damage, damage_multiplier * resist.first, damage_divisor);
879  tooltip << "<b>" << damage_with_resistance << "</b> "
880  << "<span color='" << attack_info_percent_color(resist.first-100).to_hex_string() << "'>"
881  << "<i>(" << utils::signed_percent(resist.first-100) << ")</i>"
882  << naps
883  << " : \t" // spaces to align the tab to a multiple of 8
884  << utils::join(resist.second, " " + font::unicode_bullet + " ") << '\n';
885  }
886  const string_with_tooltip damage_versus {flush(str), flush(tooltip)};
887 
888 #if 0
889  // We wanted to use the attack icon here, but couldn't find a good layout.
890  // The default images are 60x60 and have a 2-pixel transparent border. Trim it.
891  // The first SCALE() accounts for theoretically possible add-ons attack images larger than 60x60.
892  const std::string attack_icon = at.icon() + "~SCALE_INTO_SHARP(60,60)~CROP(2,2,56,56)~SCALE_INTO_SHARP(32,32)";
893  add_image(res, attack_icon, at.name());
894  add_text(res, " ", "");
895 #endif
896 
897  // The icons are 16x16. We add 5px padding for alignment reasons (placement of the icon in relation to ascender and descender letters).
898  const std::string spacer = "misc/blank.png~CROP(0, 0, 16, 21)"; // 21 == 16+5
899  if (range_png_exists) add_image(res, spacer + "~BLIT(" + range_png + ",0,5)", damage_versus.tooltip);
900  if (type_png_exists) add_image(res, spacer + "~BLIT(" + type_png + ",0,5)", damage_versus.tooltip);
901  add_text(res, damage_and_num_attacks.str, damage_and_num_attacks.tooltip);
902  add_text(res, damage_versus.str, damage_versus.tooltip); // This string is usually empty
903 
904  const std::string &accuracy_parry = at.accuracy_parry_description();
905  if (!accuracy_parry.empty())
906  {
908  << " " << accuracy_parry << "</span>\n";
909  int accuracy = at.accuracy();
910  if (accuracy) {
911  tooltip << _("Accuracy:") << "<b>"
912  << utils::signed_percent(accuracy) << "</b>\n";
913  }
914  int parry = at.parry();
915  if (parry) {
916  tooltip << _("Parry:") << "<b>"
917  << utils::signed_percent(parry) << "</b>\n";
918  }
919  add_text(res, flush(str), flush(tooltip));
920  }
921  }
922 
923  {
924  //If we have a second unit, do the 2-unit specials_context
925  bool attacking = (u.side() == rc.screen().playing_side());
926  auto ctx = (sec_u == nullptr) ? at.specials_context_for_listing(attacking) :
927  at.specials_context(unit_const_ptr(&u), unit_const_ptr(sec_u), hex, sec_u->get_location(), attacking, sec_u_weapon);
928 
929  boost::dynamic_bitset<> active;
930  const std::vector<std::pair<t_string, t_string>> &specials = at.special_tooltips(&active);
931  const std::size_t specials_size = specials.size();
932  for ( std::size_t i = 0; i != specials_size; ++i )
933  {
934  // Aliases for readability:
935  const t_string &name = specials[i].first;
936  const t_string &description = specials[i].second;
937  const color_t &details_color = active[i] ? font::weapon_details_color :
939 
940  str << span_color(details_color) << " " << " " << name << naps << '\n';
941  std::string help_page = "weaponspecial_" + name.base_str();
942  tooltip << _("Weapon special: ") << "<b>" << name << "</b>";
943  if ( !active[i] )
944  tooltip << "<i>" << _(" (inactive)") << "</i>";
945  tooltip << '\n' << description;
946 
947  add_text(res, flush(str), flush(tooltip), help_page);
948  }
949  if(!specials.empty()) {
950  // Add some padding so the end of the specials list
951  // isn't too close vertically to the attack icons of
952  // the next attack. Also for symmetry with the padding
953  // above the list of specials (below the attack icon line).
954  const std::string spacer = "misc/blank.png~CROP(0, 0, 1, 5)";
955  add_image(res, spacer, "");
956  add_text(res, "\n", "");
957  }
958  }
959  return damage;
960 }
961 
962 // Conversion routine for both unscathed and damage change percentage.
963 static std::string format_prob(double prob)
964 {
965  if(prob > 0.9995) {
966  return "100%";
967  } else if(prob < 0.0005) {
968  return "0%";
969  }
970  std::ostringstream res;
971  res << std::setprecision(prob < 0.01 ? 1 : prob < 0.1 ? 2 : 3) << 100.0 * prob << "%";
972  return res.str();
973 }
974 
975 static std::string format_hp(unsigned hp)
976 {
977  std::ostringstream res;
978  res << ' ' << std::setw(3) << hp;
979  return res.str();
980 }
981 
982 static config unit_weapons(reports::context & rc, const unit *attacker, const map_location &attacker_pos, const unit *defender, bool show_attacker)
983 {
984  if (!attacker || !defender) return config();
985 
986  const unit* u = show_attacker ? attacker : defender;
987  const unit* sec_u = !show_attacker ? attacker : defender;
988  const map_location unit_loc = show_attacker ? attacker_pos : defender->get_location();
989 
990  std::ostringstream str, tooltip;
991  config res;
992 
993  std::vector<battle_context> weapons;
994  for (unsigned int i = 0; i < attacker->attacks().size(); i++) {
995  // skip weapons with attack_weight=0
996  if (attacker->attacks()[i].attack_weight() > 0) {
997  weapons.emplace_back(rc.units(), attacker_pos, defender->get_location(), i, -1, 0.0, nullptr, attacker);
998  }
999  }
1000 
1001  for (const battle_context& weapon : weapons) {
1002 
1003  // Predict the battle outcome.
1004  combatant attacker_combatant(weapon.get_attacker_stats());
1005  combatant defender_combatant(weapon.get_defender_stats());
1006  attacker_combatant.fight(defender_combatant);
1007 
1008  const battle_context_unit_stats& context_unit_stats =
1009  show_attacker ? weapon.get_attacker_stats() : weapon.get_defender_stats();
1010  const battle_context_unit_stats& other_context_unit_stats =
1011  !show_attacker ? weapon.get_attacker_stats() : weapon.get_defender_stats();
1012 
1013  int total_damage = 0;
1014  int base_damage = 0;
1015  int num_blows = 0;
1016  int chance_to_hit = 0;
1017  t_string weapon_name = _("weapon^None");
1018 
1019  color_t dmg_color = font::weapon_color;
1020  if (context_unit_stats.weapon) {
1021  base_damage = attack_info(rc, *context_unit_stats.weapon, res, *u, unit_loc, sec_u, other_context_unit_stats.weapon);
1022  total_damage = context_unit_stats.damage;
1023  num_blows = context_unit_stats.num_blows;
1024  chance_to_hit = context_unit_stats.chance_to_hit;
1025  weapon_name = context_unit_stats.weapon->name();
1026 
1027  if ( total_damage > base_damage )
1028  dmg_color = font::good_dmg_color;
1029  else if ( total_damage < base_damage )
1030  dmg_color = font::bad_dmg_color;
1031  } else {
1032  str << span_color(font::weapon_color) << weapon_name << naps << "\n";
1033  tooltip << _("Weapon: ") << "<b>" << weapon_name << "</b>\n"
1034  << _("Damage: ") << "<b>" << "0" << "</b>\n";
1035  }
1036 
1037  color_t chance_color = game_config::red_to_green(chance_to_hit);
1038 
1039  // Total damage.
1040  str << " " << span_color(dmg_color) << total_damage << naps << span_color(font::weapon_color)
1041  << font::unicode_en_dash << num_blows
1042  << " (" << span_color(chance_color) << chance_to_hit << "%" << naps << ")"
1043  << naps << "\n";
1044 
1045  tooltip << _("Weapon: ") << "<b>" << weapon_name << "</b>\n"
1046  << _("Total damage") << "<b>" << total_damage << "</b>\n";
1047 
1048  // Create the hitpoints distribution.
1049  std::vector<std::pair<int, double>> hp_prob_vector;
1050 
1051  // First, we sort the probabilities in ascending order.
1052  std::vector<std::pair<double, int>> prob_hp_vector;
1053  int i;
1054 
1055  combatant* c = show_attacker ? &attacker_combatant : &defender_combatant;
1056 
1057  for(i = 0; i < static_cast<int>(c->hp_dist.size()); i++) {
1058  double prob = c->hp_dist[i];
1059 
1060  // We keep only values above 0.1%.
1061  if(prob > 0.001)
1062  prob_hp_vector.emplace_back(prob, i);
1063  }
1064 
1065  std::sort(prob_hp_vector.begin(), prob_hp_vector.end());
1066 
1067  //TODO fendrin -- make that dynamically
1068  int max_hp_distrib_rows_ = 10;
1069 
1070  // We store a few of the highest probability hitpoint values.
1071  int nb_elem = std::min<int>(max_hp_distrib_rows_, prob_hp_vector.size());
1072 
1073  for(i = prob_hp_vector.size() - nb_elem;
1074  i < static_cast<int>(prob_hp_vector.size()); i++) {
1075 
1076  hp_prob_vector.emplace_back(prob_hp_vector[i].second, prob_hp_vector[i].first);
1077  }
1078 
1079  // Then, we sort the hitpoint values in ascending order.
1080  std::sort(hp_prob_vector.begin(), hp_prob_vector.end());
1081  // And reverse the order. Might be doable in a better manor.
1082  std::reverse(hp_prob_vector.begin(), hp_prob_vector.end());
1083 
1084  for(i = 0; i < static_cast<int>(hp_prob_vector.size()); i++) {
1085 
1086  int hp = hp_prob_vector[i].first;
1087  double prob = hp_prob_vector[i].second;
1088  color_t prob_color = game_config::blue_to_white(prob * 100.0, true);
1089 
1090  str << span_color(font::weapon_details_color) << " " << " "
1091  << span_color(u->hp_color(hp)) << format_hp(hp) << naps
1092  << " " << font::weapon_numbers_sep << " "
1093  << span_color(prob_color) << format_prob(prob) << naps
1094  << naps << "\n";
1095  }
1096 
1097  add_text(res, flush(str), flush(tooltip));
1098  }
1099  return res;
1100 }
1101 
1102 /*
1103  * Display the attacks of the displayed unit against the unit passed as argument.
1104  * 'hex' is the location the attacker will be at during combat.
1105  */
1106 static config unit_weapons(reports::context & rc, const unit *u, const map_location &hex)
1107 {
1108  config res = config();
1109  if ((u != nullptr) && (!u->attacks().empty())) {
1110  const std::string attack_headline = _n("Attack", "Attacks", u->attacks().size());
1111 
1113  + attack_headline + "</span>" + '\n', "");
1114 
1115  const auto left = u->attacks_left(false), max = u->max_attacks();
1116  if(max != 1) {
1117  // TRANSLATORS: This string is shown in the sidebar beneath the word "Attacks" when a unit can attack multiple times per turn
1118  const std::string line = VGETTEXT("Remaining: $left/$max",
1119  {{"left", std::to_string(left)},
1120  {"max", std::to_string(max)}});
1121  add_text(res, " " + span_color(font::weapon_details_color, line) + "\n",
1122  _("This unit can attack multiple times per turn."));
1123  }
1124 
1125  for (const attack_type &at : u->attacks())
1126  {
1127  attack_info(rc, at, res, *u, hex);
1128  }
1129  }
1130  return res;
1131 }
1133 {
1134  const unit *u = get_visible_unit(rc);
1135  const map_location& mouseover_hex = rc.screen().mouseover_hex();
1136  const map_location& displayed_unit_hex = rc.screen().displayed_unit_hex();
1137  const map_location& hex = mouseover_hex.valid() ? mouseover_hex : displayed_unit_hex;
1138  if (!u) return config();
1139 
1140  return unit_weapons(rc, u, hex);
1141 }
1142 REPORT_GENERATOR(highlighted_unit_weapons, rc)
1143 {
1144  const unit *u = get_selected_unit(rc);
1145  const unit *sec_u = get_visible_unit(rc);
1146 
1147  if (!u) return report_unit_weapons(rc);
1148  if (!sec_u || u == sec_u) return unit_weapons(rc, sec_u, rc.screen().mouseover_hex());
1149 
1150  map_location highlighted_hex = rc.screen().displayed_unit_hex();
1151  map_location attack_loc;
1152  if (rc.mhb())
1153  attack_loc = rc.mhb()->current_unit_attacks_from(highlighted_hex);
1154 
1155  if (!attack_loc.valid())
1156  return unit_weapons(rc, sec_u, rc.screen().mouseover_hex());
1157 
1158  return unit_weapons(rc, u, attack_loc, sec_u, false);
1159 }
1160 REPORT_GENERATOR(selected_unit_weapons, rc)
1161 {
1162  const unit *u = get_selected_unit(rc);
1163  const unit *sec_u = get_visible_unit(rc);
1164 
1165  if (!u) return config();
1166  if (!sec_u || u == sec_u) return unit_weapons(rc, u, u->get_location());
1167 
1168  map_location highlighted_hex = rc.screen().displayed_unit_hex();
1169  map_location attack_loc;
1170  if (rc.mhb())
1171  attack_loc = rc.mhb()->current_unit_attacks_from(highlighted_hex);
1172 
1173  if (!attack_loc.valid())
1174  return unit_weapons(rc, u, u->get_location());
1175 
1176  return unit_weapons(rc, u, attack_loc, sec_u, true);
1177 }
1178 
1179 REPORT_GENERATOR(unit_image,rc)
1180 {
1181  const unit *u = get_visible_unit(rc);
1182  if (!u) return config();
1183  return image_report(u->absolute_image() + u->image_mods());
1184 }
1185 REPORT_GENERATOR(selected_unit_image, rc)
1186 {
1187  const unit *u = get_selected_unit(rc);
1188  if (!u) return config();
1189  return image_report(u->absolute_image() + u->image_mods());
1190 }
1191 
1192 REPORT_GENERATOR(selected_unit_profile, rc)
1193 {
1194  const unit *u = get_selected_unit(rc);
1195  if (!u) return config();
1196  return image_report(u->small_profile());
1197 }
1198 REPORT_GENERATOR(unit_profile, rc)
1199 {
1200  const unit *u = get_visible_unit(rc);
1201  if (!u) return config();
1202  return image_report(u->small_profile());
1203 }
1204 
1206 {
1207  std::ostringstream tooltip;
1208  std::ostringstream text;
1209 
1210  const map_location& tod_schedule_hex = (hex.valid() && !display::get_singleton()->shrouded(hex)) ? hex : map_location::null_location();
1211  const std::vector<time_of_day>& schedule = rc.tod().times(tod_schedule_hex);
1212 
1213  tooltip << _("Time of day schedule:") << " \n";
1214  int current = rc.tod().get_current_time(tod_schedule_hex);
1215  int i = 0;
1216  for (const time_of_day& tod : schedule) {
1217  if (i == current) tooltip << "<big><b>";
1218  tooltip << tod.name << "\n";
1219  if (i == current) tooltip << "</b></big>";
1220  i++;
1221  }
1222 
1223  int times = schedule.size();
1224  text << current + 1 << "/" << times;
1225 
1226  return text_report(text.str(), tooltip.str(), "..schedule");
1227 }
1228 REPORT_GENERATOR(tod_stats, rc)
1229 {
1230  map_location mouseover_hex = rc.screen().mouseover_hex();
1231  if (mouseover_hex.valid()) return tod_stats_at(rc, mouseover_hex);
1232  return tod_stats_at(rc, rc.screen().selected_hex());
1233 }
1234 REPORT_GENERATOR(selected_tod_stats, rc)
1235 {
1236  const unit *u = get_selected_unit(rc);
1237  if(!u) return tod_stats_at(rc, map_location::null_location());
1238  const map_location& attack_indicator_src = game_display::get_singleton()->get_attack_indicator_src();
1239  const map_location& hex =
1240  attack_indicator_src.valid() ? attack_indicator_src : u->get_location();
1241  return tod_stats_at(rc, hex);
1242 }
1243 
1244 static config time_of_day_at(reports::context & rc, const map_location& mouseover_hex)
1245 {
1246  std::ostringstream tooltip;
1247  time_of_day tod = get_visible_time_of_day_at(rc, mouseover_hex);
1248 
1249  int b = tod.lawful_bonus;
1250  int l = generic_combat_modifier(b, unit_type::ALIGNMENT::LIMINAL, false, rc.tod().get_max_liminal_bonus());
1251  std::string lawful_color("white");
1252  std::string chaotic_color("white");
1253  std::string liminal_color("white");
1254 
1255  // Use same red/green colouring scheme as time_of_day_bonus_colored() in help/help_impl.cpp for consistency
1256  if (b != 0) {
1257  lawful_color = (b > 0) ? "green" : "red";
1258  chaotic_color = (b < 0) ? "green" : "red";
1259  }
1260  if (l != 0) {
1261  liminal_color = (l > 0) ? "green" : "red";
1262  }
1263  tooltip << _("Time of day:") << " <b>" << tod.name << "</b>\n"
1264  << _("Lawful units: ") << "<span foreground=\"" << lawful_color << "\">"
1265  << utils::signed_percent(b) << "</span>\n"
1266  << _("Neutral units: ") << utils::signed_percent(0) << '\n'
1267  << _("Chaotic units: ") << "<span foreground=\"" << chaotic_color << "\">"
1268  << utils::signed_percent(-b) << "</span>\n"
1269  << _("Liminal units: ") << "<span foreground=\"" << liminal_color << "\">"
1270  << utils::signed_percent(l) << "</span>\n";
1271 
1272  std::string tod_image = tod.image;
1273  if(tod.bonus_modified > 0) {
1274  tod_image += (formatter() << "~BLIT(" << game_config::images::tod_bright << ")").str();
1275  } else if(tod.bonus_modified < 0) {
1276  tod_image += (formatter() << "~BLIT(" << game_config::images::tod_dark << ")").str();
1277  }
1278 
1279  return image_report(tod_image, tooltip.str(), "time_of_day_" + tod.id);
1280 }
1282 {
1283  map_location mouseover_hex = rc.screen().mouseover_hex();
1284  if (mouseover_hex.valid()) return time_of_day_at(rc, mouseover_hex);
1285  return time_of_day_at(rc, rc.screen().selected_hex());
1286 }
1287 REPORT_GENERATOR(selected_time_of_day, rc)
1288 {
1289  const unit *u = get_selected_unit(rc);
1290  if(!u) return time_of_day_at(rc, map_location::null_location());
1291  const map_location& attack_indicator_src = game_display::get_singleton()->get_attack_indicator_src();
1292  const map_location& hex =
1293  attack_indicator_src.valid() ? attack_indicator_src : u->get_location();
1294  return time_of_day_at(rc, hex);
1295 }
1296 
1297 static config unit_box_at(reports::context & rc, const map_location& mouseover_hex)
1298 {
1299  std::ostringstream tooltip;
1300  time_of_day global_tod = rc.tod().get_time_of_day();
1301  time_of_day local_tod = get_visible_time_of_day_at(rc, mouseover_hex);
1302 
1303  int bonus = local_tod.lawful_bonus;
1304  int l = generic_combat_modifier(bonus, unit_type::ALIGNMENT::LIMINAL, false, rc.tod().get_max_liminal_bonus());
1305 
1306  std::string lawful_color("white");
1307  std::string chaotic_color("white");
1308  std::string liminal_color("white");
1309 
1310  if (bonus != 0) {
1311  lawful_color = (bonus > 0) ? "green" : "red";
1312  chaotic_color = (bonus < 0) ? "green" : "red";
1313  }
1314  if (l != 0) {
1315  liminal_color = (l > 0) ? "green" : "red";
1316  }
1317  tooltip << local_tod.name << '\n'
1318  << _("Lawful units: ") << "<span foreground=\"" << lawful_color << "\">"
1319  << utils::signed_percent(bonus) << "</span>\n"
1320  << _("Neutral units: ") << utils::signed_percent(0) << '\n'
1321  << _("Chaotic units: ") << "<span foreground=\"" << chaotic_color << "\">"
1322  << utils::signed_percent(-bonus) << "</span>\n"
1323  << _("Liminal units: ") << "<span foreground=\"" << liminal_color << "\">"
1324  << utils::signed_percent(l) << "</span>\n";
1325 
1326  std::string local_tod_image = "themes/classic/" + local_tod.image;
1327  std::string global_tod_image = "themes/classic/" + global_tod.image;
1328  if(local_tod.bonus_modified != 0) {
1329  local_tod_image += "~BLIT(";
1330  if (local_tod.bonus_modified > 0) local_tod_image += game_config::images::tod_bright;
1331  else if (local_tod.bonus_modified < 0) local_tod_image += game_config::images::tod_dark;
1332  local_tod_image += ")";
1333  }
1334 
1335  const gamemap &map = rc.map();
1336  t_translation::terrain_code terrain = map.get_terrain(mouseover_hex);
1337 
1338  //if (t_translation::terrain_matches(terrain, t_translation::ALL_OFF_MAP))
1339  // return config();
1340 
1341  //if (map.is_keep(mouseover_hex)) {
1342  // add_image(cfg, "icons/terrain/terrain_type_keep.png", "");
1343  //}
1344 
1345  const t_translation::ter_list& underlying_terrains = map.underlying_union_terrain(terrain);
1346 
1347  std::string bg_terrain_image;
1348 
1349  for (const t_translation::terrain_code& underlying_terrain : underlying_terrains) {
1350  const std::string& terrain_id = map.get_terrain_info(underlying_terrain).id();
1351  bg_terrain_image = "~BLIT(unit_env/terrain/terrain-" + terrain_id + ".png)" + bg_terrain_image;
1352  }
1353 
1354  std::stringstream color;
1355  color << local_tod.color;
1356 
1357  bg_terrain_image = bg_terrain_image + "~CS(" + color.str() + ")";
1358 
1359  const unit *u = get_visible_unit(rc);
1360  std::string unit_image;
1361  if (u)
1362  unit_image = "~BLIT(" + u->absolute_image() + u->image_mods() + ",35,22)";
1363 
1364  std::string tod_image = global_tod_image + "~BLIT(" + local_tod_image + ")";
1365 
1366  return image_report(tod_image + bg_terrain_image + unit_image, tooltip.str(), "time_of_day");
1367 }
1368 REPORT_GENERATOR(unit_box, rc)
1369 {
1370  map_location mouseover_hex = rc.screen().mouseover_hex();
1371  return unit_box_at(rc, mouseover_hex);
1372 }
1373 
1374 
1376 {
1377  std::ostringstream str, tooltip;
1378  str << rc.tod().turn();
1379  int nb = rc.tod().number_of_turns();
1380  if (nb != -1) str << '/' << nb;
1381 
1382  tooltip << _("Turn Number");
1383  if(nb != -1) {
1384  tooltip << "\n\n" << _("When the game reaches the number of turns indicated by the second number, it will end.");
1385  }
1386  return text_report(str.str(), tooltip.str());
1387 }
1388 
1390 {
1391  std::ostringstream str;
1392  int viewing_side = rc.screen().viewing_side();
1393  // Suppose the full unit map is applied.
1394  int fake_gold = rc.dc().get_team(viewing_side).gold();
1395 
1396  if (rc.wb())
1397  fake_gold -= rc.wb()->get_spent_gold_for(viewing_side);
1398  char const *end = naps;
1399  if (viewing_side != rc.screen().playing_side()) {
1400  str << span_color(font::GRAY_COLOR);
1401  }
1402  else if (fake_gold < 0) {
1403  str << span_color(font::BAD_COLOR);
1404  }
1405  else {
1406  end = "";
1407  }
1408  str << utils::half_signed_value(fake_gold) << end;
1409  return text_report(str.str(), _("Gold") + "\n\n" + _("The amount of gold currently available to recruit and maintain your army."));
1410 }
1411 
1412 REPORT_GENERATOR(villages, rc)
1413 {
1414  std::ostringstream str;
1415  int viewing_side = rc.screen().viewing_side();
1416  const team &viewing_team = rc.dc().get_team(viewing_side);
1417  str << viewing_team.villages().size() << '/';
1418  if (viewing_team.uses_shroud()) {
1419  int unshrouded_villages = 0;
1420  for (const map_location &loc : rc.map().villages()) {
1421  if (!viewing_team.shrouded(loc))
1422  ++unshrouded_villages;
1423  }
1424  str << unshrouded_villages;
1425  } else {
1426  str << rc.map().villages().size();
1427  }
1428  return gray_inactive(rc,str.str(), _("Villages") + "\n\n" + _("The fraction of known villages that your side has captured."));
1429 }
1430 
1431 REPORT_GENERATOR(num_units, rc)
1432 {
1433  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."));
1434 }
1435 
1436 REPORT_GENERATOR(upkeep, rc)
1437 {
1438  std::ostringstream str;
1439  int viewing_side = rc.screen().viewing_side();
1440  const team &viewing_team = rc.dc().get_team(viewing_side);
1441  team_data td(rc.dc(), viewing_team);
1442  str << td.expenses << " (" << td.upkeep << ")";
1443  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."));
1444 }
1445 
1446 REPORT_GENERATOR(expenses, rc)
1447 {
1448  int viewing_side = rc.screen().viewing_side();
1449  const team &viewing_team = rc.dc().get_team(viewing_side);
1450  team_data td(rc.dc(), viewing_team);
1451  return gray_inactive(rc,std::to_string(td.expenses));
1452 }
1453 
1454 REPORT_GENERATOR(income, rc)
1455 {
1456  std::ostringstream str;
1457  int viewing_side = rc.screen().viewing_side();
1458  const team &viewing_team = rc.dc().get_team(viewing_side);
1459  team_data td(rc.dc(), viewing_team);
1460  char const *end = naps;
1461  if (viewing_side != rc.screen().playing_side()) {
1462  if (td.net_income < 0) {
1463  td.net_income = - td.net_income;
1464  str << span_color(font::GRAY_COLOR);
1465  str << font::unicode_minus;
1466  }
1467  else {
1468  str << span_color(font::GRAY_COLOR);
1469  }
1470  }
1471  else if (td.net_income < 0) {
1472  td.net_income = - td.net_income;
1473  str << span_color(font::BAD_COLOR);
1474  str << font::unicode_minus;
1475  }
1476  else {
1477  end = "";
1478  }
1479  str << td.net_income << end;
1480  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."));
1481 }
1482 
1483 namespace {
1484 void blit_tced_icon(config &cfg, const std::string &terrain_id, const std::string &icon_image, bool high_res,
1485  const std::string &terrain_name) {
1486  const std::string tc_base = high_res ? "images/buttons/icon-base-32.png" : "images/buttons/icon-base-16.png";
1487  const std::string terrain_image = "terrain/" + icon_image + (high_res ? "_30.png" : ".png");
1488  add_image(cfg, tc_base + "~RC(magenta>" + terrain_id + ")~BLIT(" + terrain_image + ")", terrain_name);
1489 }
1490 }
1491 
1492 REPORT_GENERATOR(terrain_info, rc)
1493 {
1494  const gamemap &map = rc.map();
1495  map_location mouseover_hex = rc.screen().mouseover_hex();
1496 
1497  if (!map.on_board(mouseover_hex))
1498  mouseover_hex = rc.screen().selected_hex();
1499 
1500  if (!map.on_board(mouseover_hex))
1501  return config();
1502 
1503  t_translation::terrain_code terrain = map.get_terrain(mouseover_hex);
1505  return config();
1506 
1507  config cfg;
1508 
1509  bool high_res = false;
1510 
1511  if (display::get_singleton()->shrouded(mouseover_hex)) {
1512  return cfg;
1513  }
1514  //TODO
1515 // if (display::get_singleton()->fogged(mouseover_hex)) {
1516 // blit_tced_icon(cfg, "fog", high_res);
1517 // }
1518 //
1519 // if (map.is_keep(mouseover_hex)) {
1520 // blit_tced_icon(cfg, "keep", high_res);
1521 // }
1522 
1523  const t_translation::ter_list& underlying_terrains = map.underlying_union_terrain(terrain);
1524  for (const t_translation::terrain_code& underlying_terrain : underlying_terrains) {
1525 
1527  continue;
1528  const std::string& terrain_id = map.get_terrain_info(underlying_terrain).id();
1529  const std::string& terrain_name = map.get_terrain_string(underlying_terrain);
1530  const std::string& terrain_icon = map.get_terrain_info(underlying_terrain).icon_image();
1531  if (terrain_icon.empty())
1532  continue;
1533  blit_tced_icon(cfg, terrain_id, terrain_icon, high_res, terrain_name);
1534  }
1535  return cfg;
1536 }
1537 
1539 {
1540  const gamemap &map = rc.map();
1541  int viewing_side = rc.screen().viewing_side();
1542  const team &viewing_team = rc.dc().get_team(viewing_side);
1543  map_location mouseover_hex = rc.screen().mouseover_hex();
1544  if (!map.on_board(mouseover_hex) || viewing_team.shrouded(mouseover_hex))
1545  return config();
1546 
1547  t_translation::terrain_code terrain = map.get_terrain(mouseover_hex);
1549  return config();
1550 
1551  std::ostringstream str;
1552  if (map.is_village(mouseover_hex))
1553  {
1554  int owner = rc.dc().village_owner(mouseover_hex) + 1;
1555  if (owner == 0 || viewing_team.fogged(mouseover_hex)) {
1556  str << map.get_terrain_info(terrain).income_description();
1557  } else if (owner == viewing_side) {
1558  str << map.get_terrain_info(terrain).income_description_own();
1559  } else if (viewing_team.is_enemy(owner)) {
1560  str << map.get_terrain_info(terrain).income_description_enemy();
1561  } else {
1562  str << map.get_terrain_info(terrain).income_description_ally();
1563  }
1564 
1565  const std::string& underlying_desc = map.get_underlying_terrain_string(terrain);
1566  if(!underlying_desc.empty()) {
1567  str << underlying_desc;
1568  }
1569  } else {
1570  str << map.get_terrain_string(terrain);
1571  }
1572 
1573  return text_report(str.str());
1574 }
1575 
1576 REPORT_GENERATOR(zoom_level, rc)
1577 {
1578  std::ostringstream text;
1579  std::ostringstream tooltip;
1580  std::ostringstream help;
1581 
1582  text << static_cast<int>(rc.screen().get_zoom_factor() * 100) << "%";
1583 
1584  return text_report(text.str(), tooltip.str(), help.str());
1585 }
1586 
1587 REPORT_GENERATOR(position, rc)
1588 {
1589  const gamemap &map = rc.map();
1590  map_location mouseover_hex = rc.screen().mouseover_hex(),
1591  displayed_unit_hex = rc.screen().displayed_unit_hex(),
1592  selected_hex = rc.screen().selected_hex();
1593 
1594  if (!map.on_board(mouseover_hex)) {
1595  if (!map.on_board(selected_hex))
1596  return config();
1597  else {
1598  mouseover_hex = selected_hex;
1599  }
1600  }
1601 
1602  t_translation::terrain_code terrain = map[mouseover_hex];
1604  return config();
1605 
1606  std::ostringstream str;
1607  str << mouseover_hex;
1608 
1609  const unit *u = get_visible_unit(rc);
1610  const team &viewing_team = rc.teams()[rc.screen().viewing_team()];
1611  if (!u ||
1612  (displayed_unit_hex != mouseover_hex &&
1613  displayed_unit_hex != rc.screen().selected_hex()) ||
1614  viewing_team.shrouded(mouseover_hex))
1615  return text_report(str.str());
1616 
1617  int move_cost = u->movement_cost(terrain);
1618  int defense = 100 - u->defense_modifier(terrain);
1619  if (move_cost < movetype::UNREACHABLE) {
1620  str << " " << defense << "%," << move_cost;
1621  } else if (mouseover_hex == displayed_unit_hex) {
1622  str << " " << defense << "%,‒";
1623  } else {
1624  str << " ‒";
1625  }
1626  return text_report(str.str());
1627 }
1628 
1629 REPORT_GENERATOR(side_playing, rc)
1630 {
1631  const team &active_team = rc.teams()[rc.screen().playing_team()];
1632  std::string flag_icon = active_team.flag_icon();
1633  std::string old_rgb = game_config::flag_rgb;
1634  std::string new_rgb = team::get_side_color_id(rc.screen().playing_side());
1635  std::string mods = "~RC(" + old_rgb + ">" + new_rgb + ")";
1636  if (flag_icon.empty())
1637  flag_icon = game_config::images::flag_icon;
1638  return image_report(flag_icon + mods, side_tooltip(active_team));
1639 }
1640 
1641 REPORT_GENERATOR(observers, rc)
1642 {
1643  const std::set<std::string> &observers = rc.screen().observers();
1644  if (observers.empty())
1645  return config();
1646 
1647  std::ostringstream str;
1648  str << _("Observers:") << '\n';
1649  for (const std::string &obs : observers) {
1650  str << obs << '\n';
1651  }
1652  return image_report(game_config::images::observer, str.str());
1653 }
1654 
1655 /* TODO unused
1656 REPORT_GENERATOR(selected_terrain)
1657 {
1658  const std::string selected_terrain = editor::get_selected_terrain();
1659  if (selected_terrain.empty())
1660  return config();
1661  else
1662  return text_report(selected_terrain);
1663 }
1664 */
1665 
1666 /* TODO this is unused
1667 REPORT_GENERATOR(edit_left_button_function)
1668 {
1669  const std::string left_button_function = editor::get_left_button_function();
1670  if (left_button_function.empty())
1671  return config();
1672  else
1673  return text_report(left_button_function);
1674 }
1675 */
1676 
1677 REPORT_GENERATOR(report_clock, /*rc*/)
1678 {
1679  config report;
1681 
1682  std::ostringstream ss;
1683 
1685  ? "%I:%M %p"
1686  : "%H:%M";
1687 
1688  std::time_t t = std::time(nullptr);
1689  ss << std::put_time(std::localtime(&t), format);
1690  add_text(report, ss.str(), _("Clock"));
1691 
1692  return report;
1693 }
1694 
1695 
1696 REPORT_GENERATOR(battery, /*rc*/)
1697 {
1698  config report;
1699 
1701  add_text(report, (boost::format("%.0f %%") % desktop::battery_info::get_battery_percentage()).str(), _("Battery"));
1702 
1703  return report;
1704 }
1705 
1706 REPORT_GENERATOR(report_countdown, rc)
1707 {
1708  int viewing_side = rc.screen().viewing_side();
1709  const team &viewing_team = rc.dc().get_team(viewing_side);
1710  int min, sec;
1711  if (viewing_team.countdown_time() == 0)
1712  return report_report_clock(rc);
1713  std::ostringstream str;
1714  sec = viewing_team.countdown_time() / 1000;
1715  char const *end = naps;
1716  if (viewing_side != rc.screen().playing_side())
1717  str << span_color(font::GRAY_COLOR);
1718  else if (sec < 60)
1719  str << "<span foreground=\"#c80000\">";
1720  else if (sec < 120)
1721  str << "<span foreground=\"#c8c800\">";
1722  else
1723  end = "";
1724  min = sec / 60;
1725  str << min << ':';
1726  sec = sec % 60;
1727  if (sec < 10) str << '0';
1728  str << sec << end;
1729  return text_report(str.str(), _("Turn Countdown") + "\n\n" + _("Countdown until your turn automatically ends."));
1730 }
1731 
1732 void reports::register_generator(const std::string &name, reports::generator *g)
1733 {
1734  dynamic_generators_[name].reset(g);
1735 }
1736 
1737 config reports::generate_report(const std::string &name, reports::context & rc, bool only_static)
1738 {
1739  if (!only_static) {
1740  dynamic_report_generators::const_iterator i = dynamic_generators_.find(name);
1741  if (i != dynamic_generators_.end())
1742  return i->second->generate(rc);
1743  }
1744  static_report_generators::const_iterator j = static_generators.find(name);
1745  if (j != static_generators.end())
1746  return j->second(rc);
1747  return config();
1748 }
1749 
1750 const std::set<std::string> &reports::report_list()
1751 {
1752  if (!all_reports_.empty()) return all_reports_;
1753  for (const static_report_generators::value_type &v : static_generators) {
1754  all_reports_.insert(v.first);
1755  }
1756  for (const dynamic_report_generators::value_type &v : dynamic_generators_) {
1757  all_reports_.insert(v.first);
1758  }
1759  return all_reports_;
1760 }
const_attack_ptr weapon
The weapon used by the unit to attack the opponent, or nullptr if there is none.
Definition: attack.hpp:50
static config unit_level(const unit *u)
Definition: reports.cpp:261
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:969
static config unit_xp(const unit *u)
Definition: reports.cpp:498
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:536
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:1369
int vision() const
Gets the unit&#39;s vision points.
Definition: unit.hpp:1363
const std::string weapon_details_sep
Definition: constants.cpp:46
This class represents a single unit of a specific type.
Definition: unit.hpp:106
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:1244
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:1403
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:525
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:127
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:1355
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:439
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:473
static void add_status(config &r, char const *path, char const *desc1, char const *desc2)
Definition: reports.cpp:82
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:1564
static config image_report(const std::string &image, const std::string &tooltip="", const std::string &help="")
Definition: reports.cpp:72
bool show_everything() const
Definition: display.hpp:90
const std::set< std::string > & report_list()
Definition: reports.cpp:1750
static std::string format_hp(unsigned hp)
Definition: reports.cpp:975
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:49
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:935
The unit is poisoned - it loses health each turn.
Definition: unit.hpp:839
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:64
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:963
The unit is petrified - it cannot move or be attacked.
Definition: unit.hpp:840
std::string absolute_image() const
The name of the file to game_display (used in menus).
Definition: unit.cpp:2424
const std::string & type() const
Definition: attack_type.hpp:42
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
const std::string & type_id() const
The id of this unit&#39;s type.
Definition: unit.hpp:334
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:1598
static config unit_side(reports::context &rc, const unit *u)
Definition: reports.cpp:229
static config unit_name(const unit *u)
Definition: reports.cpp:151
const tod_manager & tod() const
Definition: reports.hpp:56
static config unit_traits(const unit *u)
Definition: reports.cpp:289
report_generator_helper(const char *name, reports::generator_function g)
Definition: reports.cpp:116
static config unit_type(const unit *u)
Definition: reports.cpp:179
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:1021
display & screen()
Definition: reports.hpp:55
static config tod_stats_at(reports::context &rc, const map_location &hex)
Definition: reports.cpp:1205
int num_attacks() const
Definition: attack_type.hpp:51
unit_type::ALIGNMENT alignment() const
The alignment of this unit.
Definition: unit.hpp:449
unsigned int chance_to_hit
Effective chance to hit as a percentage (all factors accounted for).
Definition: attack.hpp:73
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:310
A single unit type that the player may recruit.
Definition: types.hpp:42
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:143
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:391
const unit_type & type() const
This unit&#39;s type, accounting for gender and variation.
Definition: unit.hpp:323
static static_report_generators static_generators
Definition: reports.cpp:112
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:1124
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:200
int defense_modifier(const t_translation::terrain_code &terrain) const
The unit&#39;s defense on a given terrain.
Definition: unit.cpp:1649
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:74
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:353
const std::string & id() const
Gets this unit&#39;s id.
Definition: unit.hpp:351
bool uses_shroud() const
Definition: team.hpp:318
The unit is slowed - it moves slower and does less damage.
Definition: unit.hpp:838
const terrain_code FOGGED
#define REPORT_GENERATOR(n, cn)
Definition: reports.cpp:122
void register_generator(const std::string &name, generator *)
Definition: reports.cpp:1732
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:97
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:1178
const t_string & name() const
Definition: attack_type.hpp:40
config generate_report(const std::string &name, context &ct, bool only_static=false)
Definition: reports.cpp:1737
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:374
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:171
const terrain_code PLUS
int max_experience() const
The max number of experience points this unit can have.
Definition: unit.hpp:503
std::set< t_translation::terrain_code > & encountered_terrains()
Definition: game.cpp:958
const std::string & range() const
Definition: attack_type.hpp:44
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:776
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:533
const std::vector< t_string > & trait_names() const
Gets the names of the currently registered traits.
Definition: unit.hpp:1055
const std::string & icon() const
Definition: attack_type.hpp:43
static config unit_hp(reports::context &rc, const unit *u)
Definition: reports.cpp:445
Structure describing the statistics of a unit involved in the battle.
Definition: attack.hpp:48
const t_string & type_name() const
Gets the translatable name of this unit&#39;s type.
Definition: unit.hpp:340
std::string small_profile() const
An optional profile image to display in Help.
Definition: unit.cpp:1063
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
static const ::config * terrain
The terrain used to create the cache.
Definition: minimap.cpp:130
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:319
Encapsulates the map of the game.
Definition: location.hpp:42
static const unit * get_visible_unit(reports::context &rc)
Definition: reports.cpp:129
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:38
static config unit_box_at(reports::context &rc, const map_location &mouseover_hex)
Definition: reports.cpp:1297
bool invisible(const map_location &loc, bool see_all=true) const
Definition: unit.cpp:2452
All combat-related info.
static std::string side_tooltip(const team &team)
Definition: reports.cpp:219
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:50
int max_hitpoints() const
The max number of hitpoints this unit can have.
Definition: unit.hpp:479
int accuracy() const
Definition: attack_type.hpp:48
std::string resistance_color(const int resistance)
Definition: helper.cpp:35
static const unit * get_selected_unit(reports::context &rc)
Definition: reports.cpp:136
static void add_image(config &report, const std::string &image, const std::string &tooltip, const std::string &help="")
Definition: reports.cpp:55
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:726
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:903
std::map< std::string, std::string > advancement_icons() const
Gets and image path and and associated description for each advancement option.
Definition: unit.cpp:1717
std::string accuracy_parry_description() const
Definition: attack_type.cpp:79
static std::string flush(std::ostringstream &s)
Definition: reports.cpp:90
double get_battery_percentage()
int modified_damage(bool is_backstab) const
Returns the damage per attack of this weapon, considering specials.
Definition: abilities.cpp:962
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:467
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:111
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:1210
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:497
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:1320
symbol_table string_table
Definition: language.cpp:63
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:46
this module manages the cache of images.
bool can_advance() const
Checks whether this unit has any options to advance to.
Definition: unit.hpp:239
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:421
static const map_location & null_location()
Definition: location.hpp:85
int total_movement() const
The maximum moves this unit has.
Definition: unit.hpp:1229
unsigned int num_blows
Effective number of blows, takes swarm into account.
Definition: attack.hpp:78
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:49
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:982
int side() const
The side this unit belongs to.
Definition: unit.hpp:311
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:1692
std::vector< std::pair< std::string, std::string > > amla_icons() const
Gets the image and description data for modification advancements.
Definition: unit.cpp:1757
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:914
int movement_left() const
Gets how far a unit can move, considering the incapacitated flag.
Definition: unit.hpp:1245
std::string image_mods() const
Gets an IPF string containing all IPF image mods.
Definition: unit.cpp:2608
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:953
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:1065
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:1578
const color_t inactive_ability_color