The Battle for Wesnoth  1.13.11+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
help_topic_generators.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 http://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 
16 
17 #include "font/sdl_ttf.hpp" // for line_width
18 #include "game_config.hpp" // for debug, menu_contract, etc
19 #include "preferences/game.hpp" // for encountered_terrains, etc
20 #include "gettext.hpp" // for _, gettext, N_
21 #include "language.hpp" // for string_table, symbol_table
22 #include "log.hpp" // for LOG_STREAM, logger, etc
23 #include "movetype.hpp" // for movetype, movetype::effects, etc
24 #include "units/race.hpp" // for unit_race, etc
25 #include "terrain/terrain.hpp" // for terrain_type
26 #include "terrain/translation.hpp" // for operator==, ter_list, etc
27 #include "terrain/type_data.hpp" // for terrain_type_data, etc
28 #include "tstring.hpp" // for t_string, operator<<
29 #include "units/helper.hpp" // for resistance_color
30 #include "units/types.hpp" // for unit_type, unit_type_data, etc
31 #include "video.hpp" // fore current_resolution
32 
33 #include <boost/optional.hpp> // for optional
34 #include <iostream> // for operator<<, basic_ostream, etc
35 #include <map> // for map, etc
36 #include <set>
37 #include <SDL.h>
38 
39 static lg::log_domain log_help("help");
40 #define WRN_HP LOG_STREAM(warn, log_help)
41 #define DBG_HP LOG_STREAM(debug, log_help)
42 
43 namespace help {
44 
45 static std::string best_str(bool best) {
46  std::string lang_policy = (best ? _("Best of") : _("Worst of"));
47  std::string color_policy = (best ? "green": "red");
48 
49  return "<format>color='" + color_policy + "' text='" + lang_policy + "'</format>";
50 }
51 
52 typedef t_translation::ter_list::const_iterator ter_iter;
53 // Gets an english description of a terrain ter_list alias behavior: "Best of cave, hills", "Worst of Swamp, Forest" etc.
54 static std::string print_behavior_description(ter_iter start, ter_iter end, const ter_data_cache & tdata, bool first_level = true, bool begin_best = true)
55 {
56 
57  if (start == end) return "";
58  if (*start == t_translation::MINUS || *start == t_translation::PLUS) return print_behavior_description(start+1, end, tdata, first_level, *start == t_translation::PLUS); //absorb any leading mode changes by calling again, with a new default value begin_best.
59 
60  boost::optional<ter_iter> last_change_pos;
61 
62  bool best = begin_best;
63  for (ter_iter i = start; i != end; ++i) {
64  if ((best && *i == t_translation::MINUS) || (!best && *i == t_translation::PLUS)) {
65  best = !best;
66  last_change_pos = i;
67  }
68  }
69 
70  std::stringstream ss;
71 
72  if (!last_change_pos) {
73  std::vector<std::string> names;
74  for (ter_iter i = start; i != end; ++i) {
75  const terrain_type tt = tdata->get_terrain_info(*i);
76  if (!tt.editor_name().empty())
77  names.push_back(tt.editor_name());
78  }
79 
80  if (names.empty()) return "";
81  if (names.size() == 1) return names.at(0);
82 
83  ss << best_str(best) << " ";
84  if (!first_level) ss << "( ";
85  ss << names.at(0);
86 
87  for (size_t i = 1; i < names.size(); i++) {
88  ss << ", " << names.at(i);
89  }
90 
91  if (!first_level) ss << " )";
92  } else {
93  std::vector<std::string> names;
94  for (ter_iter i = *last_change_pos+1; i != end; ++i) {
95  const terrain_type tt = tdata->get_terrain_info(*i);
96  if (!tt.editor_name().empty())
97  names.push_back(tt.editor_name());
98  }
99 
100  if (names.empty()) { //This alias list is apparently padded with junk at the end, so truncate it without adding more parens
101  return print_behavior_description(start, *last_change_pos, tdata, first_level, begin_best);
102  }
103 
104  ss << best_str(best) << " ";
105  if (!first_level) ss << "( ";
106  ss << print_behavior_description(start, *last_change_pos-1, tdata, false, begin_best);
107  // Printed the (parenthesized) leading part from before the change, now print the remaining names in this group.
108  for (const std::string & s : names) {
109  ss << ", " << s;
110  }
111  if (!first_level) ss << " )";
112  }
113  return ss.str();
114 }
115 
117  std::stringstream ss;
118 
119  if (!type_.icon_image().empty())
120  ss << "<img>src='images/buttons/icon-base-32.png~RC(magenta>" << type_.id()
121  << ")~BLIT("<< "terrain/" << type_.icon_image() << "_30.png)" << "'</img> ";
122 
123  if (!type_.editor_image().empty())
124  ss << "<img>src='" << type_.editor_image() << "'</img> ";
125 
126  if (!type_.help_topic_text().empty())
127  ss << "\n\n" << type_.help_topic_text().str() << "\n";
128  else
129  ss << "\n";
130 
132 
133  if (!tdata) {
134  WRN_HP << "When building terrain help topics, we couldn't acquire any terrain types data\n";
135  return ss.str();
136  }
137 
138  if (!(type_.union_type().size() == 1 && type_.union_type()[0] == type_.number() && type_.is_nonnull())) {
139 
140  const t_translation::ter_list& underlying_mvt_terrains = tdata->underlying_mvt_terrain(type_.number());
141 
142  ss << "\n" << _("Base Terrain: ");
143 
144  bool first = true;
145  for (const t_translation::terrain_code& underlying_terrain : underlying_mvt_terrains) {
146  const terrain_type& mvt_base = tdata->get_terrain_info(underlying_terrain);
147 
148  if (mvt_base.editor_name().empty()) continue;
149 
150  if (!first) {
151  ss << ", ";
152  } else {
153  first = false;
154  }
155 
156  ss << make_link(mvt_base.editor_name(), ".." + terrain_prefix + mvt_base.id());
157  }
158 
159  ss << "\n";
160 
161  ss << "\n" << _("Movement properties: ");
162  ss << print_behavior_description(underlying_mvt_terrains.begin(), underlying_mvt_terrains.end(), tdata) << "\n";
163 
164  const t_translation::ter_list& underlying_def_terrains = tdata->underlying_def_terrain(type_.number());
165  ss << "\n" << _("Defense properties: ");
166  ss << print_behavior_description(underlying_def_terrains.begin(), underlying_def_terrains.end(), tdata) << "\n";
167  }
168 
169  if (game_config::debug) {
170 
171  ss << "\n";
172  ss << "ID: " << type_.id() << "\n";
173 
174  ss << "Village: " << (type_.is_village() ? "Yes" : "No") << "\n";
175  ss << "Gives Healing: " << type_.gives_healing() << "\n";
176 
177  ss << "Keep: " << (type_.is_keep() ? "Yes" : "No") << "\n";
178  ss << "Castle: " << (type_.is_castle() ? "Yes" : "No") << "\n";
179 
180  ss << "Overlay: " << (type_.is_overlay() ? "Yes" : "No") << "\n";
181  ss << "Combined: " << (type_.is_combined() ? "Yes" : "No") << "\n";
182  ss << "Nonnull: " << (type_.is_nonnull() ? "Yes" : "No") << "\n";
183 
184  ss << "Terrain string:" << type_.number() << "\n";
185 
186  ss << "Hide in Editor: " << (type_.hide_in_editor() ? "Yes" : "No") << "\n";
187  ss << "Hide Help: " << (type_.hide_help() ? "Yes" : "No") << "\n";
188  ss << "Editor Group: " << type_.editor_group() << "\n";
189 
190  ss << "Light Bonus: " << type_.light_bonus(0) << "\n";
191 
192  ss << type_.income_description();
193 
194  if (type_.editor_image().empty()) { // Note: this is purely temporary to help make a different help entry
195  ss << "\nEditor Image: Empty\n";
196  } else {
197  ss << "\nEditor Image: " << type_.editor_image() << "\n";
198  }
199 
200  const t_translation::ter_list& underlying_mvt_terrains = tdata->underlying_mvt_terrain(type_.number());
201  ss << "\nDebug Mvt Description String:";
202  for (const t_translation::terrain_code & t : underlying_mvt_terrains) {
203  ss << " " << t;
204  }
205 
206  const t_translation::ter_list& underlying_def_terrains = tdata->underlying_def_terrain(type_.number());
207  ss << "\nDebug Def Description String:";
208  for (const t_translation::terrain_code & t : underlying_def_terrains) {
209  ss << " " << t;
210  }
211 
212  }
213 
214  return ss.str();
215 }
216 
217 
218 //Typedef to help with formatting list of traits
219 typedef std::pair<std::string, std::string> trait_data;
220 
221 //Helper function for printing a list of trait data
222 static void print_trait_list(std::stringstream & ss, const std::vector<trait_data> & l)
223 {
224  size_t i = 0;
225  ss << make_link(l[i].first, l[i].second);
226 
227  // This doesn't skip traits with empty names
228  for(i++; i < l.size(); i++) {
229  ss << ", " << make_link(l[i].first,l[i].second);
230  }
231 }
232 
234  // Force the lazy loading to build this unit.
236 
237  std::stringstream ss;
238  std::string clear_stringstream;
239  const std::string detailed_description = type_.unit_description();
242 
244 
245  ss << "Level " << type_.level();
246  ss << "\n\n";
247 
248  ss << "<img>src='" << male_type.image();
249  ss << "~RC(" << male_type.flag_rgb() << ">red)";
250  if (screen_width >= 1200) ss << "~XBRZ(2)";
251  ss << "' box='no'</img> ";
252 
253 
254  if (&female_type != &male_type) {
255  ss << "<img>src='" << female_type.image();
256  ss << "~RC(" << female_type.flag_rgb() << ">red)";
257  if (screen_width >= 1200) ss << "~XBRZ(2)";
258  ss << "' box='no'</img> ";
259  }
260 
261  const std::string &male_portrait = male_type.small_profile().empty() ?
262  male_type.big_profile() : male_type.small_profile();
263  const std::string &female_portrait = female_type.small_profile().empty() ?
264  female_type.big_profile() : female_type.small_profile();
265 
266  const bool has_male_portrait = !male_portrait.empty() && male_portrait != male_type.image() && male_portrait != "unit_image";
267  const bool has_female_portrait = !female_portrait.empty() && female_portrait != male_portrait && female_portrait != female_type.image() && female_portrait != "unit_image";
268 
269  int sz = (has_male_portrait && has_female_portrait ? 300 : 400);
270  if (screen_width <= 1366) {
271  sz = (has_male_portrait && has_female_portrait ? 200 : 300);
272  } else if (screen_width >= 1920) {
273  sz = 400;
274  }
275 
276  // TODO: figure out why the second checks don't match but the last does
277  if (has_male_portrait) {
278  ss << "<img>src='" << male_portrait << "~FL(horiz)~SCALE_INTO(" << sz << ',' << sz << ")' box='no' align='right' float='yes'</img> ";
279  }
280 
281 
282  if (has_female_portrait) {
283  ss << "<img>src='" << female_portrait << "~FL(horiz)~SCALE_INTO(" << sz << ',' << sz << ")' box='no' align='right' float='yes'</img> ";
284  }
285 
286  ss << "\n";
287 
288  // Print cross-references to units that this unit advances from/to.
289  // Cross reference to the topics containing information about those units.
290  const bool first_reverse_value = true;
291  bool reverse = first_reverse_value;
292  if (variation_.empty()) {
293  do {
294  std::vector<std::string> adv_units =
295  reverse ? type_.advances_from() : type_.advances_to();
296  bool first = true;
297 
298  for (const std::string &adv : adv_units) {
300  if (!type || type->hide_help()) {
301  continue;
302  }
303 
304  if (first) {
305  if (reverse) {
306  ss << _("Advances from: ");
307  } else {
308  ss << _("Advances to: ");
309  }
310  first = false;
311  } else {
312  ss << ", ";
313  }
314 
315  std::string lang_unit = type->type_name();
316  std::string ref_id;
317  if (description_type(*type) == FULL_DESCRIPTION) {
318  const std::string section_prefix = type->show_variations_in_help() ? ".." : "";
319  ref_id = section_prefix + unit_prefix + type->id();
320  } else {
321  ref_id = unknown_unit_topic;
322  lang_unit += " (?)";
323  }
324  ss << make_link(lang_unit, ref_id);
325  }
326  if (!first) {
327  ss << "\n";
328  }
329 
330  reverse = !reverse; //switch direction
331  } while(reverse != first_reverse_value); // don't restart
332  }
333 
334  const unit_type* parent = variation_.empty() ? &type_ :
336  if (!variation_.empty()) {
337  ss << _("Base unit: ") << make_link(parent->type_name(), ".." + unit_prefix + type_.id()) << "\n";
338  } else {
339  bool first = true;
340  for (const std::string& base_id : utils::split(type_.get_cfg()["base_ids"])) {
341  if (first) {
342  ss << _("Base units: ");
343  first = false;
344  }
345  const unit_type* base_type = unit_types.find(base_id, unit_type::HELP_INDEXED);
346  const std::string section_prefix = base_type->show_variations_in_help() ? ".." : "";
347  ss << make_link(base_type->type_name(), section_prefix + unit_prefix + base_id) << "\n";
348  }
349  }
350 
351  bool first = true;
352  for (const std::string &var_id : parent->variations()) {
353  const unit_type &type = parent->get_variation(var_id);
354 
355  if(type.hide_help()) {
356  continue;
357  }
358 
359  if (first) {
360  ss << _("Variations: ");
361  first = false;
362  } else {
363  ss << ", ";
364  }
365 
366  std::string ref_id;
367 
368  std::string var_name = type.variation_name();
369  if (description_type(type) == FULL_DESCRIPTION) {
370  ref_id = variation_prefix + type.id() + "_" + var_id;
371  } else {
372  ref_id = unknown_unit_topic;
373  var_name += " (?)";
374  }
375 
376  ss << make_link(var_name, ref_id);
377  }
378  ss << "\n"; //added even if empty, to avoid shifting
379 
380  // Print the race of the unit, cross-reference it to the respective topic.
381  const std::string race_id = type_.race_id();
382  std::string race_name = type_.race()->plural_name();
383  if (race_name.empty()) {
384  race_name = _ ("race^Miscellaneous");
385  }
386  ss << _("Race: ");
387  ss << make_link(race_name, "..race_" + race_id);
388  ss << "\n\n";
389 
390  // Print the possible traits of the unit, cross-reference them
391  // to their respective topics.
393  std::vector<trait_data> must_have_traits;
394  std::vector<trait_data> random_traits;
395  int must_have_nameless_traits = 0;
396 
397  for (const config & trait : traits) {
398  const std::string trait_name = trait["male_name"];
399  std::string lang_trait_name = translation::gettext(trait_name.c_str());
400  if (lang_trait_name.empty() && trait["availability"].str() == "musthave") {
401  ++must_have_nameless_traits;
402  continue;
403  }
404  const std::string ref_id = "traits_"+trait["id"].str();
405  ((trait["availability"].str() == "musthave") ? must_have_traits : random_traits).emplace_back(lang_trait_name, ref_id);
406  }
407 
408  bool line1 = !must_have_traits.empty();
409  bool line2 = !random_traits.empty() && type_.num_traits() > must_have_traits.size();
410 
411  if (line1) {
412  std::string traits_label = _("Traits");
413  ss << traits_label;
414  if (line2) {
415  std::stringstream must_have_count;
416  must_have_count << " (" << must_have_traits.size() << ") : ";
417  std::stringstream random_count;
418  random_count << " (" << (type_.num_traits() - must_have_traits.size() - must_have_nameless_traits) << ") : ";
419 
420  int second_line_whitespace = font::line_width(traits_label+must_have_count.str(), normal_font_size)
421  - font::line_width(random_count.str(), normal_font_size);
422  // This ensures that the second line is justified so that the ':' characters are aligned.
423 
424  ss << must_have_count.str();
425  print_trait_list(ss, must_have_traits);
426  ss << "\n" << jump(second_line_whitespace) << random_count.str();
427  print_trait_list(ss, random_traits);
428  } else {
429  ss << ": ";
430  print_trait_list(ss, must_have_traits);
431  }
432  ss << "\n\n";
433  } else {
434  if (line2) {
435  ss << _("Traits") << " (" << (type_.num_traits() - must_have_nameless_traits) << ") : ";
436  print_trait_list(ss, random_traits);
437  ss << "\n\n";
438  }
439  }
440  }
441 
442  // Print the abilities the units has, cross-reference them
443  // to their respective topics. TODO: Update this according to musthave trait effects, similar to movetype below
444  if(!type_.abilities_metadata().empty()) {
445  ss << _("Abilities: ");
446 
447  for(auto iter = type_.abilities_metadata().begin(); iter != type_.abilities_metadata().end(); ++iter) {
448  const std::string ref_id = ability_prefix + iter->id + iter->name.base_str();
449 
450  std::string lang_ability = translation::gettext(iter->name.c_str());
451  ss << make_link(lang_ability, ref_id);
452 
453  if(std::next(iter) != type_.abilities_metadata().end()) {
454  ss << ", ";
455  }
456  }
457 
458  ss << "\n\n";
459  }
460 
461  // Print the extra AMLA upgrade abilities, cross-reference them to their respective topics.
462  if(!type_.adv_abilities_metadata().empty()) {
463  ss << _("Ability Upgrades: ");
464 
465  for(auto iter = type_.adv_abilities_metadata().begin(); iter != type_.adv_abilities_metadata().end(); ++iter) {
466  const std::string ref_id = ability_prefix + iter->id + iter->name.base_str();
467 
468  std::string lang_ability = translation::gettext(iter->name.c_str());
469  ss << make_link(lang_ability, ref_id);
470 
471  if(std::next(iter) != type_.adv_abilities_metadata().end()) {
472  ss << ", ";
473  }
474  }
475 
476  ss << "\n\n";
477  }
478 
479  // Print some basic information such as HP and movement points.
480  // TODO: Make this info update according to musthave traits, similar to movetype below.
481  ss << _("HP: ") << type_.hitpoints() << jump(30)
482  << _("Moves: ") << type_.movement() << jump(30);
483  if (type_.vision() != type_.movement()) {
484  ss << _("Vision: ") << type_.vision() << jump(30);
485  }
486  if (type_.jamming() > 0) {
487  ss << _("Jamming: ") << type_.jamming() << jump(30);
488  }
489  ss << _("Cost: ") << type_.cost() << jump(30)
490  << _("Alignment: ")
491  << make_link(type_.alignment_description(type_.alignment(), type_.genders().front()), "time_of_day")
492  << jump(30);
493  if (type_.can_advance()) {
494  ss << _("Required XP: ") << type_.experience_needed();
495  }
496 
497  // Print the detailed description about the unit.
498  ss << "\n\n" << detailed_description;
499 
500  // Print the different attacks a unit has, if it has any.
501  if (!type_.attacks().empty()) {
502  // Print headers for the table.
503  ss << "\n\n<header>text='" << escape(_("unit help^Attacks"))
504  << "'</header>\n\n";
505  table_spec table;
506 
507  std::vector<item> first_row;
508  // Dummy element, icons are below.
509  first_row.push_back(item("", 0));
510  push_header(first_row, _("unit help^Name"));
511  push_header(first_row, _("Type"));
512  push_header(first_row, _("Strikes"));
513  push_header(first_row, _("Range"));
514  push_header(first_row, _("Special"));
515  table.push_back(first_row);
516  // Print information about every attack.
517  for(const attack_type& attack : type_.attacks()) {
518  std::string lang_weapon = attack.name();
519  std::string lang_type = string_table["type_" + attack.type()];
520  std::vector<item> row;
521  std::stringstream attack_ss;
522  attack_ss << "<img>src='" << attack.icon() << "'</img>";
523  row.emplace_back(attack_ss.str(),image_width(attack.icon()));
524  push_tab_pair(row, lang_weapon);
525  push_tab_pair(row, lang_type);
526  attack_ss.str(clear_stringstream);
527  attack_ss << attack.damage() << font::unicode_en_dash << attack.num_attacks()
528  << " " << attack.accuracy_parry_description();
529  push_tab_pair(row, attack_ss.str());
530  attack_ss.str(clear_stringstream);
531  if (attack.min_range() > 1 || attack.max_range() > 1) {
532  attack_ss << attack.min_range() << "-" << attack.max_range() << ' ';
533  }
534  attack_ss << string_table["range_" + attack.range()];
535  push_tab_pair(row, attack_ss.str());
536  attack_ss.str(clear_stringstream);
537  // Show this attack's special, if it has any. Cross
538  // reference it to the section describing the special.
539  std::vector<std::pair<t_string, t_string>> specials = attack.special_tooltips();
540  if (!specials.empty()) {
541  std::string lang_special = "";
542  const size_t specials_size = specials.size();
543  for (size_t i = 0; i != specials_size; ++i) {
544  const std::string ref_id = std::string("weaponspecial_")
545  + specials[i].first.base_str();
546  lang_special = (specials[i].first);
547  attack_ss << make_link(lang_special, ref_id);
548  if (i+1 != specials_size) {
549  attack_ss << ", "; //comma placed before next special
550  }
551  }
552  row.emplace_back(attack_ss.str(), font::line_width(lang_special, normal_font_size));
553  }
554  table.push_back(row);
555  }
556  ss << generate_table(table);
557  }
558 
559  // Generate the movement type of the unit, with resistance, defense, movement, jamming and vision data updated according to any 'musthave' traits which always apply
560  movetype movement_type = type_.movement_type();
562  if (!traits.empty() && type_.num_traits() > 0) {
563  for (const config & t : traits) {
564  if (t["availability"].str() == "musthave") {
565  for (const config & effect : t.child_range("effect")) {
566  if (!effect.child("filter") // If this is musthave but has a unit filter, it might not always apply, so don't apply it in the help.
567  && movetype::effects.find(effect["apply_to"].str()) != movetype::effects.end()) {
568  movement_type.merge(effect, effect["replace"].to_bool());
569  }
570  }
571  }
572  }
573  }
574 
575  // Print the resistance table of the unit.
576  ss << "\n\n<header>text='" << escape(_("Resistances"))
577  << "'</header>\n\n";
578  table_spec resistance_table;
579  std::vector<item> first_res_row;
580  push_header(first_res_row, _("Attack Type"));
581  push_header(first_res_row, _("Resistance"));
582  resistance_table.push_back(first_res_row);
583  utils::string_map dam_tab = movement_type.damage_table();
584  for(std::pair<std::string, std::string> dam_it : dam_tab) {
585  std::vector<item> row;
586  int resistance = 100;
587  try {
588  resistance -= std::stoi(dam_it.second);
589  } catch(std::invalid_argument&) {}
590  std::string resist = std::to_string(resistance) + '%';
591  const size_t pos = resist.find('-');
592  if (pos != std::string::npos) {
593  resist.replace(pos, 1, font::unicode_minus);
594  }
595  std::string color = unit_helper::resistance_color(resistance);
596  std::string lang_weapon = string_table["type_" + dam_it.first];
597  push_tab_pair(row, lang_weapon);
598  std::stringstream str;
599  str << "<format>color=\"" << color << "\" text='"<< resist << "'</format>";
600  const std::string markup = str.str();
601  str.str(clear_stringstream);
602  str << resist;
603  row.emplace_back(markup, font::line_width(str.str(), normal_font_size));
604  resistance_table.push_back(row);
605  }
606  ss << generate_table(resistance_table);
607 
608  if (ter_data_cache tdata = load_terrain_types_data()) {
609  // Print the terrain modifier table of the unit.
610  ss << "\n\n<header>text='" << escape(_("Terrain Modifiers"))
611  << "'</header>\n\n";
612  std::vector<item> first_row;
613  table_spec table;
614  push_header(first_row, _("Terrain"));
615  push_header(first_row, _("Defense"));
616  push_header(first_row, _("Movement Cost"));
617 
618  const bool has_terrain_defense_caps = movement_type.has_terrain_defense_caps(preferences::encountered_terrains());
619  if (has_terrain_defense_caps) {
620  push_header(first_row, _("Defense Cap"));
621  }
622 
623  const bool has_vision = type_.movement_type().has_vision_data();
624  if (has_vision) {
625  push_header(first_row, _("Vision Cost"));
626  }
627  const bool has_jamming = type_.movement_type().has_jamming_data();
628  if (has_jamming) {
629  push_header(first_row, _("Jamming Cost"));
630  }
631 
632  table.push_back(first_row);
633  std::set<t_translation::terrain_code>::const_iterator terrain_it =
635 
636  for (; terrain_it != preferences::encountered_terrains().end();
637  ++terrain_it) {
638  const t_translation::terrain_code terrain = *terrain_it;
640  continue;
641  }
642  const terrain_type& info = tdata->get_terrain_info(terrain);
643  const int moves = movement_type.movement_cost(terrain);
644  const bool cannot_move = moves > type_.movement();
645  if (cannot_move && info.hide_if_impassable()) {
646  continue;
647  }
648 
649  if (info.union_type().size() == 1 && info.union_type()[0] == info.number() && info.is_nonnull()) {
650  std::vector<item> row;
651  const std::string& name = info.name();
652  const std::string& id = info.id();
653  const int views = movement_type.vision_cost(terrain);
654  const int jams = movement_type.jamming_cost(terrain);
655 
656  bool high_res = false;
657  const std::string tc_base = high_res ? "images/buttons/icon-base-32.png" : "images/buttons/icon-base-16.png";
658  const std::string terrain_image = "icons/terrain/terrain_type_" + id + (high_res ? "_30.png" : ".png");
659 
660  const std::string final_image = tc_base + "~RC(magenta>" + id + ")~BLIT(" + terrain_image + ")";
661 
662  row.emplace_back("<img>src='" + final_image + "'</img> " +
663  make_link(name, "..terrain_" + id),
664  font::line_width(name, normal_font_size) + (high_res ? 32 : 16) );
665 
666  //defense - range: +10 % .. +70 %
667  const int defense = 100 - movement_type.defense_modifier(terrain);
668  std::string color;
669  if (defense <= 10) {
670  color = "red";
671  } else if (defense <= 30) {
672  color = "yellow";
673  } else if (defense <= 50) {
674  color = "white";
675  } else {
676  color = "green";
677  }
678 
679  std::stringstream str;
680  str << "<format>color=" << color << " text='"<< defense << "%'</format>";
681  std::string markup = str.str();
682  str.str(clear_stringstream);
683  str << defense << "%";
684  row.emplace_back(markup, font::line_width(str.str(), normal_font_size));
685 
686  //movement - range: 1 .. 5, movetype::UNREACHABLE=impassable
687  str.str(clear_stringstream);
688  if (cannot_move) { // cannot move in this terrain
689  color = "red";
690  } else if (moves > 1) {
691  color = "yellow";
692  } else {
693  color = "white";
694  }
695  str << "<format>color=" << color << " text='";
696  // A 5 MP margin; if the movement costs go above
697  // the unit's max moves + 5, we replace it with dashes.
698  if(cannot_move && (moves > type_.movement() + 5)) {
700  } else {
701  str << moves;
702  }
703  str << "'</format>";
704  markup = str.str();
705  str.str(clear_stringstream);
706  str << moves;
707  row.emplace_back(markup, font::line_width(str.str(), normal_font_size));
708 
709  //defense cap
710  if (has_terrain_defense_caps) {
711  str.str(clear_stringstream);
712  const bool has_cap = movement_type.get_defense().capped(terrain);
713  if (has_cap) {
714  str << "<format>color='"<< color <<"' text='" << defense << "%'</format>";
715  } else {
716  str << "<format>color=white text='" << font::unicode_figure_dash << "'</format>";
717  }
718  markup = str.str();
719  str.str(clear_stringstream);
720  if (has_cap) {
721  str << defense << '%';
722  } else {
724  }
725  row.emplace_back(markup, font::line_width(str.str(), normal_font_size));
726  }
727 
728  //vision
729  if (has_vision) {
730  str.str(clear_stringstream);
731  const bool cannot_view = views > type_.vision();
732  if (cannot_view) { // cannot view in this terrain
733  color = "red";
734  } else if (views > moves) {
735  color = "yellow";
736  } else if (views == moves) {
737  color = "white";
738  } else {
739  color = "green";
740  }
741  str << "<format>color=" << color << " text='";
742  // A 5 MP margin; if the vision costs go above
743  // the unit's vision + 5, we replace it with dashes.
744  if(cannot_view && (views > type_.vision() + 5)) {
746  } else {
747  str << views;
748  }
749  str << "'</format>";
750  markup = str.str();
751  str.str(clear_stringstream);
752  str << views;
753  row.emplace_back(markup, font::line_width(str.str(), normal_font_size));
754  }
755 
756  //jamming
757  if (has_jamming) {
758  str.str(clear_stringstream);
759  const bool cannot_jam = jams > type_.jamming();
760  if (cannot_jam) { // cannot jamm in this terrain
761  color = "red";
762  } else if (jams > views) {
763  color = "yellow";
764  } else if (jams == views) {
765  color = "white";
766  } else {
767  color = "green";
768  }
769  str << "<format>color=" << color << " text='";
770  // A 5 MP margin; if the jamming costs go above
771  // the unit's jamming + 5, we replace it with dashes.
772  if ( cannot_jam && jams > type_.jamming() + 5 ) {
774  } else {
775  str << jams;
776  }
777  str << "'</format>";
778 
779  push_tab_pair(row, str.str());
780  }
781 
782  table.push_back(row);
783  }
784  }
785 
786  ss << generate_table(table);
787  } else {
788  WRN_HP << "When building unit help topics, the display object was null and we couldn't get the terrain info we need.\n";
789  }
790  return ss.str();
791 }
792 
793 void unit_topic_generator::push_header(std::vector< item > &row, const std::string& name) const {
794  row.emplace_back(bold(name), font::line_width(name, normal_font_size, TTF_STYLE_BOLD));
795 }
796 
797 } // end namespace help
const std::string ability_prefix
Definition: help_impl.cpp:95
int movement_cost(const t_translation::terrain_code &terrain, bool slowed=false) const
Returns the cost to move through the indicated terrain.
Definition: movetype.hpp:197
static const std::set< std::string > effects
The set of applicable effects for movement types.
Definition: movetype.hpp:231
std::vector< char_t > string
const t_translation::ter_list & union_type() const
Definition: terrain.hpp:50
const t_string & help_topic_text() const
Definition: terrain.hpp:36
const std::string unit_prefix
Definition: help_impl.cpp:89
std::map< std::string, t_string > string_map
static lg::log_domain log_help("help")
const unit_type & get_gender_unit_type(std::string gender) const
Returns a gendered variant of this unit_type.
Definition: types.cpp:446
const t_string & type_name() const
The name of the unit in the current language setting.
Definition: types.hpp:136
int movement() const
Definition: types.hpp:152
bool capped(const t_translation::terrain_code &terrain) const
Returns whether there is a defense cap associated to this terrain.
Definition: movetype.hpp:132
const std::vector< std::string > advances_from() const
A vector of unit_type ids that can advance to this unit_type.
Definition: types.cpp:677
virtual std::string operator()() const
const std::string unknown_unit_topic
Definition: help_impl.cpp:88
bool terrain_matches(const terrain_code &src, const terrain_code &dest)
Tests whether a specific terrain matches an expression, for matching rules see above.
logger & info()
Definition: log.cpp:91
const movetype & movement_type() const
Definition: types.hpp:174
const_attack_itors attacks() const
Definition: types.cpp:495
const std::string & variation_name() const
Definition: types.hpp:160
bool hide_in_editor() const
Definition: terrain.hpp:40
int jamming() const
Definition: types.hpp:156
const t_string & name() const
Definition: terrain.hpp:33
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
bool is_overlay() const
Definition: terrain.hpp:79
unit_type_data unit_types
Definition: types.cpp:1453
const std::string & id() const
Definition: terrain.hpp:37
const unit_race * race() const
Never returns nullptr, but may point to the null race.
Definition: types.hpp:265
const std::string & image() const
Definition: types.hpp:162
static std::string print_behavior_description(ter_iter start, ter_iter end, const ter_data_cache &tdata, bool first_level=true, bool begin_best=true)
static CVideo & get_singleton()
Definition: video.hpp:43
void push_header(std::vector< item > &row, const std::string &name) const
bool is_combined() const
Definition: terrain.hpp:80
int light_bonus(int base) const
Returns the light (lawful) bonus for this terrain when the time of day gives a base bonus...
Definition: terrain.hpp:56
const int normal_font_size
Definition: help_impl.cpp:81
void merge(const config &new_cfg, bool overwrite=true)
Merges the given config over the existing data.
Definition: movetype.cpp:750
The basic "size" of the unit - flying, small land, large land, etc.
Definition: movetype.hpp:26
unsigned image_width(const std::string &filename)
Definition: help_impl.cpp:1481
const std::set< std::string > movetype::effects resistance
Definition: movetype.cpp:782
const std::string & editor_group() const
Definition: terrain.hpp:76
int level() const
Definition: types.hpp:150
const std::string terrain_prefix
Definition: help_impl.cpp:90
const std::vector< ability_metadata > & abilities_metadata() const
Definition: types.hpp:209
-file util.hpp
const std::string & flag_rgb() const
Definition: types.cpp:745
const t_string & editor_name() const
Definition: terrain.hpp:34
std::vector< std::string > split(const std::string &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
std::string bold(const std::string &s)
Definition: help_impl.hpp:396
A single unit type that the player may recruit.
Definition: types.hpp:42
bool can_advance() const
Definition: types.hpp:214
const terrain_code VOID_TERRAIN
bool has_terrain_defense_caps(const std::set< t_translation::terrain_code > &ts) const
Returns whether or not there are any terrain caps with respect to a set of terrains.
Definition: movetype.cpp:738
static std::string best_str(bool best)
bool has_vision_data() const
Returns whether or not there are any vision-specific costs.
Definition: movetype.hpp:223
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:89
const std::string unicode_minus
Definition: constants.cpp:37
std::string generate_table(const table_spec &tab, const unsigned int spacing)
Definition: help_impl.cpp:1496
int jamming_cost(const t_translation::terrain_code &terrain, bool slowed=false) const
Returns the cost to "jam" through the indicated terrain.
Definition: movetype.hpp:203
int gives_healing() const
Definition: terrain.hpp:64
const terrain_code FOGGED
const std::string & big_profile() const
Definition: types.hpp:165
const std::string & editor_image() const
Definition: terrain.hpp:32
int get_width(bool as_pixels=true) const
Returns the window renderer width in pixels or screen coordinates.
Definition: video.cpp:316
terrain_defense & get_defense()
Definition: movetype.hpp:182
std::vector< std::vector< std::pair< std::string, unsigned int > > > table_spec
Definition: help_impl.hpp:403
const terrain_code MINUS
const std::vector< unit_race::GENDER > & genders() const
The returned vector will not be empty, provided this has been built to the HELP_INDEXED status...
Definition: types.hpp:239
bool show_variations_in_help() const
Whether the unit type has at least one help-visible variation.
Definition: types.cpp:787
t_translation::terrain_code number() const
Definition: terrain.hpp:44
void build_unit_type(const unit_type &ut, unit_type::BUILD_STATUS status) const
Makes sure the provided unit_type is built to the specified level.
Definition: types.hpp:372
const terrain_code PLUS
std::set< t_translation::terrain_code > & encountered_terrains()
Definition: game.cpp:946
t_translation::ter_list::const_iterator ter_iter
ter_data_cache load_terrain_types_data()
Load the appropriate terrain types data to use.
Definition: help_impl.cpp:1545
const t_string & plural_name() const
Definition: race.hpp:35
static UNUSEDNOWARN std::string gettext(const char *str)
Definition: gettext.hpp:64
static const ::config * terrain
The terrain used to create the cache.
Definition: minimap.cpp:130
bool is_village() const
Definition: terrain.hpp:65
const std::string unicode_figure_dash
Definition: constants.cpp:40
const std::string & small_profile() const
Definition: types.hpp:164
#define WRN_HP
bool is_nonnull() const
Definition: terrain.hpp:52
utils::string_map damage_table() const
Returns a map from attack types to resistances.
Definition: movetype.hpp:217
bool hide_help() const
Definition: terrain.hpp:39
std::string race_id() const
Returns the ID of this type's race without the need to build the type.
Definition: types.hpp:262
int cost() const
Definition: types.hpp:158
boost::iterator_range< const_child_iterator > const_child_itors
Definition: config.hpp:239
const std::vector< std::string > & advances_to() const
A vector of unit_type ids that this unit_type can advance to.
Definition: types.hpp:113
static void print_trait_list(std::stringstream &ss, const std::vector< trait_data > &l)
const std::string variation_prefix
Definition: help_impl.cpp:94
std::pair< std::string, unsigned > item
const std::string unicode_en_dash
Definition: constants.cpp:38
std::string resistance_color(const int resistance)
Definition: helper.cpp:35
bool is_keep() const
Definition: terrain.hpp:67
const std::set< std::string > movetype::effects defense
Definition: movetype.cpp:782
const unit_type & get_variation(const std::string &id) const
Definition: types.cpp:467
config::const_child_itors possible_traits() const
Definition: types.hpp:223
EXIT_STATUS start(const config &game_conf, const std::string &filename, bool take_screenshot, const std::string &screenshot_filename)
Main interface for launching the editor from the title screen.
Definition: editor_main.cpp:28
std::string make_link(const std::string &text, const std::string &dst)
Definition: help_impl.hpp:376
static map_location::DIRECTION s
std::vector< std::string > names
Definition: build_info.cpp:60
int vision() const
Definition: types.hpp:153
size_t i
Definition: function.cpp:933
std::vector< std::string > variations() const
Definition: types.cpp:770
unsigned screen_width
The screen resolution should be available for all widgets since their drawing method will depend on i...
Definition: settings.cpp:25
bool hide_help() const
Definition: types.cpp:588
virtual std::string operator()() const
const config & get_cfg() const
Definition: types.hpp:269
std::string escape(const std::string &s)
Prepend all chars with meaning inside attributes with a backslash.
Definition: help_impl.cpp:1539
int hitpoints() const
Definition: types.hpp:147
int experience_needed(bool with_acceleration=true) const
Definition: types.cpp:529
#define next(ls)
Definition: llex.cpp:32
std::string jump(const unsigned amount)
Definition: help_impl.hpp:389
const ter_match ALL_OFF_MAP
double t
Definition: astarsearch.cpp:64
symbol_table string_table
Definition: language.cpp:62
bool has_jamming_data() const
Returns whether or not there are any jamming-specific costs.
Definition: movetype.hpp:225
Standard logging facilities (interface).
std::vector< terrain_code > ter_list
Definition: translation.hpp:77
static const char * name(const std::vector< SDL_Joystick * > &joysticks, const size_t index)
Definition: joystick.cpp:48
unsigned int num_traits() const
Definition: types.hpp:133
const unit_type * find(const std::string &key, unit_type::BUILD_STATUS status=unit_type::FULL) const
Finds a unit_type by its id() and makes sure it is built to the specified level.
Definition: types.cpp:1276
static void reverse(lua_State *L, StkId from, StkId to)
Definition: lapi.cpp:193
Definition: help.cpp:56
const std::string & str() const
Definition: tstring.hpp:186
int vision_cost(const t_translation::terrain_code &terrain, bool slowed=false) const
Returns the cost to see through the indicated terrain.
Definition: movetype.hpp:200
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:93
static std::string alignment_description(ALIGNMENT align, unit_race::GENDER gender=unit_race::MALE)
bool is_castle() const
Definition: terrain.hpp:66
UNIT_DESCRIPTION_TYPE description_type(const unit_type &type)
Return the type of description that should be shown for a unit of the given kind. ...
Definition: help_impl.cpp:1003
bool hide_if_impassable() const
Definition: terrain.hpp:41
const t_string & income_description() const
Definition: terrain.hpp:71
int line_width(const std::string &line, int font_size, int style)
Determine the width of a line of text given a certain font size.
Definition: sdl_ttf.cpp:416
const std::vector< ability_metadata > & adv_abilities_metadata() const
Some extra abilities that may be gained through AMLA advancements.
Definition: types.hpp:212
int defense_modifier(const t_translation::terrain_code &terrain) const
Returns the defensive value of the indicated terrain.
Definition: movetype.hpp:207
const std::string & icon_image() const
Definition: terrain.hpp:29
bool empty() const
Definition: tstring.hpp:182
std::pair< std::string, std::string > trait_data
const std::string & id() const
The id for this unit_type.
Definition: types.hpp:139
void push_tab_pair(std::vector< std::pair< std::string, unsigned int >> &v, const std::string &s)
Definition: help_impl.cpp:1491
t_string unit_description() const
Definition: types.cpp:477
std::shared_ptr< terrain_type_data > ter_data_cache