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