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