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