The Battle for Wesnoth  1.13.4+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 - 2016 by David White <[email protected]>
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 "game_config.hpp" // for debug, menu_contract, etc
18 #include "game_preferences.hpp" // for encountered_terrains, etc
19 #include "gettext.hpp" // for _, gettext, N_
20 #include "language.hpp" // for string_table, symbol_table
21 #include "log.hpp" // for LOG_STREAM, logger, etc
22 #include "movetype.hpp" // for movetype, movetype::effects, etc
23 #include "units/race.hpp" // for unit_race, etc
24 #include "terrain/terrain.hpp" // for terrain_type
25 #include "terrain/translation.hpp" // for operator==, t_list, etc
26 #include "terrain/type_data.hpp" // for terrain_type_data, etc
27 #include "tstring.hpp" // for t_string, operator<<
28 #include "units/helper.hpp" // for resistance_color
29 #include "units/types.hpp" // for unit_type, unit_type_data, etc
30 #include "video.hpp" // fore current_resolution
31 
32 #include <boost/optional.hpp> // for optional
33 #include <boost/shared_ptr.hpp> // for shared_ptr
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::t_list::const_iterator t_it;
53 // Gets an english desription of a terrain t_list alias behavior: "Best of cave, hills", "Worst of Swamp, Forest" etc.
54 static std::string print_behavior_description(t_it start, t_it end, const tdata_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<t_it> last_change_pos;
61 
62  bool best = begin_best;
63  for (t_it 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 (t_it 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.size() == 0) 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 (t_it 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::t_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::t_terrain& 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::t_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::t_list& underlying_mvt_terrains = tdata->underlying_mvt_terrain(type_.number());
201  ss << "\nDebug Mvt Description String:";
202  for (const t_translation::t_terrain & t : underlying_mvt_terrains) {
203  ss << " " << t;
204  }
205 
206  const t_translation::t_list& underlying_def_terrains = tdata->underlying_def_terrain(type_.number());
207  ss << "\nDebug Def Description String:";
208  for (const t_translation::t_terrain & 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  for(i++; i < l.size(); i++) {
228  ss << ", " << make_link(l[i].first,l[i].second);
229  }
230 }
231 
233  // Force the lazy loading to build this unit.
235 
236  std::stringstream ss;
237  std::string clear_stringstream;
238  const std::string detailed_description = type_.unit_description();
241 
242  ss << "Level " << type_.level();
243  ss << "\n\n";
244 
245  // Show the unit's image.
246 #ifdef LOW_MEM
247  ss << "<img>src='" << male_type.image() << "~XBRZ(2)' box='no'</img> ";
248 #else
249  ss << "<img>src='" << male_type.image() << "~RC(" << male_type.flag_rgb() << ">red)~XBRZ(2)' box='no'</img> ";
250 #endif
251 
252  if (&female_type != &male_type) {
253 #ifdef LOW_MEM
254  ss << "<img>src='" << female_type.image() << "~XBRZ(2)' box='no'</img> ";
255 #else
256  ss << "<img>src='" << female_type.image() << "~RC(" << female_type.flag_rgb() << ">red)~XBRZ(2)' box='no'</img> ";
257 #endif
258  }
259 
260  const std::string &male_portrait = male_type.small_profile().empty() ?
261  male_type.big_profile() : male_type.small_profile();
262  const std::string &female_portrait = female_type.small_profile().empty() ?
263  female_type.big_profile() : female_type.small_profile();
264 
265  int sz = 400;
267  if (screen_width <= 800) {
268  sz = 200;
269  } else if(screen_width <= 1024) {
270  sz = 300;
271  }
272 
273  // TODO: figure out why the second checks don't match but the last does
274  if (!male_portrait.empty() && male_portrait != male_type.image() && male_portrait != "unit_image") {
275  ss << "<img>src='" << male_portrait << "~FL(horiz)~SCALE_INTO(" << sz << ',' << sz << ")' box='no' align='right' float='yes'</img> ";
276  }
277 
278  ss << "\n\n";
279 
280  if (!female_portrait.empty() && female_portrait != male_portrait && female_portrait != female_type.image() && female_portrait != "unit_image") {
281  ss << "<img>src='" << female_portrait << "~FL(horiz)~SCALE_INTO(" << sz << ',' << sz << ")' box='no' align='right' float='yes'</img> ";
282  }
283 
284  ss << "\n";
285 
286  // Print cross-references to units that this unit advances from/to.
287  // Cross reference to the topics containing information about those units.
288  const bool first_reverse_value = true;
289  bool reverse = first_reverse_value;
290  if (variation_.empty()) {
291  do {
292  std::vector<std::string> adv_units =
293  reverse ? type_.advances_from() : type_.advances_to();
294  bool first = true;
295 
296  for (const std::string &adv : adv_units) {
298  if (!type || type->hide_help()) {
299  continue;
300  }
301 
302  if (first) {
303  if (reverse) {
304  ss << _("Advances from: ");
305  } else {
306  ss << _("Advances to: ");
307  }
308  first = false;
309  } else {
310  ss << ", ";
311  }
312 
313  std::string lang_unit = type->type_name();
314  std::string ref_id;
315  if (description_type(*type) == FULL_DESCRIPTION) {
316  const std::string section_prefix = type->show_variations_in_help() ? ".." : "";
317  ref_id = section_prefix + unit_prefix + type->id();
318  } else {
319  ref_id = unknown_unit_topic;
320  lang_unit += " (?)";
321  }
322  ss << make_link(lang_unit, ref_id);
323  }
324  if (!first) {
325  ss << "\n";
326  }
327 
328  reverse = !reverse; //switch direction
329  } while(reverse != first_reverse_value); // don't restart
330  }
331 
332  const unit_type* parent = variation_.empty() ? &type_ :
334  if (!variation_.empty()) {
335  ss << _("Base unit: ") << make_link(parent->type_name(), ".." + unit_prefix + type_.id()) << "\n";
336  } else {
337  bool first = true;
338  for (const std::string& base_id : utils::split(type_.get_cfg()["base_ids"])) {
339  if (first) {
340  ss << _("Base units: ");
341  first = false;
342  }
343  const unit_type* base_type = unit_types.find(base_id, unit_type::HELP_INDEXED);
344  const std::string section_prefix = base_type->show_variations_in_help() ? ".." : "";
345  ss << make_link(base_type->type_name(), section_prefix + unit_prefix + base_id) << "\n";
346  }
347  }
348 
349  bool first = true;
350  for (const std::string &var_id : parent->variations()) {
351  const unit_type &type = parent->get_variation(var_id);
352 
353  if(type.hide_help()) {
354  continue;
355  }
356 
357  if (first) {
358  ss << _("Variations: ");
359  first = false;
360  } else {
361  ss << ", ";
362  }
363 
364  std::string ref_id;
365 
366  std::string var_name = type.variation_name();
367  if (description_type(type) == FULL_DESCRIPTION) {
368  ref_id = variation_prefix + type.id() + "_" + var_id;
369  } else {
370  ref_id = unknown_unit_topic;
371  var_name += " (?)";
372  }
373 
374  ss << make_link(var_name, ref_id);
375  }
376  ss << "\n"; //added even if empty, to avoid shifting
377 
378  // Print the race of the unit, cross-reference it to the respective topic.
379  const std::string race_id = type_.race_id();
380  std::string race_name = type_.race()->plural_name();
381  if (race_name.empty()) {
382  race_name = _ ("race^Miscellaneous");
383  }
384  ss << _("Race: ");
385  ss << make_link(race_name, "..race_" + race_id);
386  ss << "\n\n";
387 
388  // Print the possible traits of the unit, cross-reference them
389  // to their respective topics.
391  if (traits.first != traits.second && type_.num_traits() > 0) {
392  std::vector<trait_data> must_have_traits;
393  std::vector<trait_data> random_traits;
394 
395  for (const config & trait : traits) {
396  const std::string trait_name = trait["male_name"];
397  std::string lang_trait_name = translation::gettext(trait_name.c_str());
398  const std::string ref_id = "traits_"+trait["id"].str();
399  ((trait["availability"].str() == "musthave") ? must_have_traits : random_traits).push_back(std::make_pair(lang_trait_name, ref_id));
400  }
401 
402  bool line1 = !must_have_traits.empty();
403  bool line2 = !random_traits.empty() && type_.num_traits() - must_have_traits.size() > 0;
404 
405  if (line1) {
406  std::string traits_label = _("Traits");
407  ss << traits_label;
408  if (line2) {
409  std::stringstream must_have_count;
410  must_have_count << " (" << must_have_traits.size() << ") : ";
411  std::stringstream random_count;
412  random_count << " (" << (type_.num_traits() - must_have_traits.size()) << ") : ";
413 
414  int second_line_whitespace = font::line_width(traits_label+must_have_count.str(), normal_font_size)
415  - font::line_width(random_count.str(), normal_font_size);
416  // This ensures that the second line is justified so that the ':' characters are aligned.
417 
418  ss << must_have_count.str();
419  print_trait_list(ss, must_have_traits);
420  ss << "\n" << jump(second_line_whitespace) << random_count.str();
421  print_trait_list(ss, random_traits);
422  } else {
423  ss << ": ";
424  print_trait_list(ss, must_have_traits);
425  }
426  ss << "\n\n";
427  } else {
428  if (line2) {
429  ss << _("Traits") << " (" << type_.num_traits() << ") : ";
430  print_trait_list(ss, random_traits);
431  ss << "\n\n";
432  }
433  }
434  }
435 
436  // Print the abilities the units has, cross-reference them
437  // to their respective topics. TODO: Update this according to musthave trait effects, similar to movetype below
438  if (!type_.abilities().empty()) {
439  ss << _("Abilities: ");
440  for(std::vector<t_string>::const_iterator ability_it = type_.abilities().begin(),
441  ability_end = type_.abilities().end();
442  ability_it != ability_end; ++ability_it) {
443  const std::string ref_id = "ability_" + ability_it->base_str();
444  std::string lang_ability = translation::gettext(ability_it->c_str());
445  ss << make_link(lang_ability, ref_id);
446  if (ability_it + 1 != ability_end) {
447  ss << ", ";
448  }
449  }
450  ss << "\n\n";
451  }
452 
453  // Print the extra AMLA upgrade abilities, cross-reference them to their respective topics.
454  if (!type_.adv_abilities().empty()) {
455  ss << _("Ability Upgrades: ");
456  for(std::vector<t_string>::const_iterator ability_it = type_.adv_abilities().begin(),
457  ability_end = type_.adv_abilities().end();
458  ability_it != ability_end; ++ability_it) {
459  const std::string ref_id = "ability_" + ability_it->base_str();
460  std::string lang_ability = translation::gettext(ability_it->c_str());
461  ss << make_link(lang_ability, ref_id);
462  if (ability_it + 1 != ability_end) {
463  ss << ", ";
464  }
465  }
466  ss << "\n\n";
467  }
468 
469  // Print some basic information such as HP and movement points.
470  // TODO: Make this info update according to musthave traits, similar to movetype below.
471  ss << _("HP: ") << type_.hitpoints() << jump(30)
472  << _("Moves: ") << type_.movement() << jump(30);
473  if (type_.vision() != type_.movement()) {
474  ss << _("Vision: ") << type_.vision() << jump(30);
475  }
476  if (type_.jamming() > 0) {
477  ss << _("Jamming: ") << type_.jamming() << jump(30);
478  }
479  ss << _("Cost: ") << type_.cost() << jump(30)
480  << _("Alignment: ")
481  << make_link(type_.alignment_description(type_.alignment(), type_.genders().front()), "time_of_day")
482  << jump(30);
483  if (type_.can_advance()) {
484  ss << _("Required XP: ") << type_.experience_needed();
485  }
486 
487  // Print the detailed description about the unit.
488  ss << "\n\n" << detailed_description;
489 
490  // Print the different attacks a unit has, if it has any.
491  std::vector<attack_type> attacks = type_.attacks();
492  if (!attacks.empty()) {
493  // Print headers for the table.
494  ss << "\n\n<header>text='" << escape(_("unit help^Attacks"))
495  << "'</header>\n\n";
497 
498  std::vector<item> first_row;
499  // Dummy element, icons are below.
500  first_row.push_back(item("", 0));
501  push_header(first_row, _("unit help^Name"));
502  push_header(first_row, _("Type"));
503  push_header(first_row, _("Strikes"));
504  push_header(first_row, _("Range"));
505  push_header(first_row, _("Special"));
506  table.push_back(first_row);
507  // Print information about every attack.
508  for(std::vector<attack_type>::const_iterator attack_it = attacks.begin(),
509  attack_end = attacks.end();
510  attack_it != attack_end; ++attack_it) {
511  std::string lang_weapon = attack_it->name();
512  std::string lang_type = string_table["type_" + attack_it->type()];
513  std::vector<item> row;
514  std::stringstream attack_ss;
515  attack_ss << "<img>src='" << (*attack_it).icon() << "'</img>";
516  row.push_back(std::make_pair(attack_ss.str(),image_width(attack_it->icon())));
517  push_tab_pair(row, lang_weapon);
518  push_tab_pair(row, lang_type);
519  attack_ss.str(clear_stringstream);
520  attack_ss << attack_it->damage() << utils::unicode_en_dash << attack_it->num_attacks()
521  << " " << attack_it->accuracy_parry_description();
522  push_tab_pair(row, attack_ss.str());
523  attack_ss.str(clear_stringstream);
524  if ((*attack_it).min_range() > 1 || (*attack_it).max_range() > 1) {
525  attack_ss << (*attack_it).min_range() << "-" << (*attack_it).max_range() << ' ';
526  }
527  attack_ss << string_table["range_" + (*attack_it).range()];
528  push_tab_pair(row, attack_ss.str());
529  attack_ss.str(clear_stringstream);
530  // Show this attack's special, if it has any. Cross
531  // reference it to the section describing the special.
532  std::vector<std::pair<t_string, t_string> > specials = attack_it->special_tooltips();
533  if (!specials.empty()) {
534  std::string lang_special = "";
535  const size_t specials_size = specials.size();
536  for (size_t i = 0; i != specials_size; ++i) {
537  const std::string ref_id = std::string("weaponspecial_")
538  + specials[i].first.base_str();
539  lang_special = (specials[i].first);
540  attack_ss << make_link(lang_special, ref_id);
541  if (i+1 != specials_size) {
542  attack_ss << ", "; //comma placed before next special
543  }
544  }
545  row.push_back(std::make_pair(attack_ss.str(),
546  font::line_width(lang_special, normal_font_size)));
547  }
548  table.push_back(row);
549  }
550  ss << generate_table(table);
551  }
552 
553  // Generate the movement type of the unit, with resistance, defense, movement, jamming and vision data updated according to any 'musthave' traits which always apply
554  movetype movement_type = type_.movement_type();
555  traits = type_.possible_traits();
556  if (traits.first != traits.second && type_.num_traits() > 0) {
557  for (const config & t : traits) {
558  if (t["availability"].str() == "musthave") {
559  for (const config & effect : t.child_range("effect")) {
560  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.
561  && movetype::effects.find(effect["apply_to"].str()) != movetype::effects.end()) {
562  movement_type.merge(effect, effect["replace"].to_bool());
563  }
564  }
565  }
566  }
567  }
568 
569  // Print the resistance table of the unit.
570  ss << "\n\n<header>text='" << escape(_("Resistances"))
571  << "'</header>\n\n";
572  table_spec resistance_table;
573  std::vector<item> first_res_row;
574  push_header(first_res_row, _("Attack Type"));
575  push_header(first_res_row, _("Resistance"));
576  resistance_table.push_back(first_res_row);
577  utils::string_map dam_tab = movement_type.damage_table();
578  for (utils::string_map::const_iterator dam_it = dam_tab.begin(), dam_end = dam_tab.end();
579  dam_it != dam_end; ++dam_it) {
580  std::vector<item> row;
581  int resistance = 100 - std::stoi((*dam_it).second);
582  char resi[16];
583  snprintf(resi,sizeof(resi),"% 4d%%",resistance); // range: -100% .. +70%
584  std::string resist = resi;
585  const size_t pos = resist.find('-');
586  if (pos != std::string::npos) {
587  resist.replace(pos, 1, utils::unicode_minus);
588  }
590  std::string lang_weapon = string_table["type_" + dam_it->first];
591  push_tab_pair(row, lang_weapon);
592  std::stringstream str;
593  str << "<format>color=\"" << color << "\" text='"<< resist << "'</format>";
594  const std::string markup = str.str();
595  str.str(clear_stringstream);
596  str << resist;
597  row.push_back(std::make_pair(markup,
598  font::line_width(str.str(), normal_font_size)));
599  resistance_table.push_back(row);
600  }
601  ss << generate_table(resistance_table);
602 
603  if (tdata_cache tdata = load_terrain_types_data()) {
604  // Print the terrain modifier table of the unit.
605  ss << "\n\n<header>text='" << escape(_("Terrain Modifiers"))
606  << "'</header>\n\n";
607  std::vector<item> first_row;
609  push_header(first_row, _("Terrain"));
610  push_header(first_row, _("Defense"));
611  push_header(first_row, _("Movement Cost"));
612 
613  const bool has_terrain_defense_caps = movement_type.has_terrain_defense_caps(preferences::encountered_terrains());
614  if (has_terrain_defense_caps) {
615  push_header(first_row, _("Defense Cap"));
616  }
617 
618  const bool has_vision = type_.movement_type().has_vision_data();
619  if (has_vision) {
620  push_header(first_row, _("Vision Cost"));
621  }
622  const bool has_jamming = type_.movement_type().has_jamming_data();
623  if (has_jamming) {
624  push_header(first_row, _("Jamming Cost"));
625  }
626 
627  table.push_back(first_row);
628  std::set<t_translation::t_terrain>::const_iterator terrain_it =
630 
631  for (; terrain_it != preferences::encountered_terrains().end();
632  ++terrain_it) {
633  const t_translation::t_terrain terrain = *terrain_it;
634  if (terrain == t_translation::FOGGED || terrain == t_translation::VOID_TERRAIN || terrain == t_translation::OFF_MAP_USER) {
635  continue;
636  }
637  const terrain_type& info = tdata->get_terrain_info(terrain);
638 
639  if (info.union_type().size() == 1 && info.union_type()[0] == info.number() && info.is_nonnull()) {
640  std::vector<item> row;
641  const std::string& name = info.name();
642  const std::string& id = info.id();
643  const int moves = movement_type.movement_cost(terrain);
644  const int views = movement_type.vision_cost(terrain);
645  const int jams = movement_type.jamming_cost(terrain);
646 
647  bool high_res = false;
648  const std::string tc_base = high_res ? "images/buttons/icon-base-32.png" : "images/buttons/icon-base-16.png";
649  const std::string terrain_image = "icons/terrain/terrain_type_" + id + (high_res ? "_30.png" : ".png");
650 
651  const std::string final_image = tc_base + "~RC(magenta>" + id + ")~BLIT(" + terrain_image + ")";
652 
653  row.push_back(std::make_pair( "<img>src='" + final_image + "'</img> " +
654  make_link(name, "..terrain_" + id),
655  font::line_width(name, normal_font_size) + (high_res ? 32 : 16) ));
656 
657  //defense - range: +10 % .. +70 %
658  const int defense = 100 - movement_type.defense_modifier(terrain);
660  if (defense <= 10) {
661  color = "red";
662  } else if (defense <= 30) {
663  color = "yellow";
664  } else if (defense <= 50) {
665  color = "white";
666  } else {
667  color = "green";
668  }
669 
670  std::stringstream str;
671  str << "<format>color=" << color << " text='"<< defense << "%'</format>";
672  std::string markup = str.str();
673  str.str(clear_stringstream);
674  str << defense << "%";
675  row.push_back(std::make_pair(markup,
676  font::line_width(str.str(), normal_font_size)));
677 
678  //movement - range: 1 .. 5, movetype::UNREACHABLE=impassable
679  str.str(clear_stringstream);
680  const bool cannot_move = moves > type_.movement();
681  if (cannot_move) { // cannot move in this terrain
682  color = "red";
683  } else if (moves > 1) {
684  color = "yellow";
685  } else {
686  color = "white";
687  }
688  str << "<format>color=" << color << " text='";
689  // A 5 MP margin; if the movement costs go above
690  // the unit's max moves + 5, we replace it with dashes.
691  if(cannot_move && (moves > type_.movement() + 5)) {
693  } else {
694  str << moves;
695  }
696  str << "'</format>";
697  markup = str.str();
698  str.str(clear_stringstream);
699  str << moves;
700  row.push_back(std::make_pair(markup,
701  font::line_width(str.str(), normal_font_size)));
702 
703  //defense cap
704  if (has_terrain_defense_caps) {
705  str.str(clear_stringstream);
706  const bool has_cap = movement_type.get_defense().capped(terrain);
707  if (has_cap) {
708  str << "<format>color='"<< color <<"' text='" << defense << "%'</format>";
709  } else {
710  str << "<format>color=white text='" << utils::unicode_figure_dash << "'</format>";
711  }
712  markup = str.str();
713  str.str(clear_stringstream);
714  if (has_cap) {
715  str << defense << '%';
716  } else {
718  }
719  row.push_back(std::make_pair(markup,
720  font::line_width(str.str(), normal_font_size)));
721  }
722 
723  //vision
724  if (has_vision) {
725  str.str(clear_stringstream);
726  const bool cannot_view = views > type_.vision();
727  if (cannot_view) { // cannot view in this terrain
728  color = "red";
729  } else if (views > moves) {
730  color = "yellow";
731  } else if (views == moves) {
732  color = "white";
733  } else {
734  color = "green";
735  }
736  str << "<format>color=" << color << " text='";
737  // A 5 MP margin; if the vision costs go above
738  // the unit's vision + 5, we replace it with dashes.
739  if(cannot_view && (views > type_.vision() + 5)) {
741  } else {
742  str << views;
743  }
744  str << "'</format>";
745  markup = str.str();
746  str.str(clear_stringstream);
747  str << views;
748  row.push_back(std::make_pair(markup,
749  font::line_width(str.str(), normal_font_size)));
750  }
751 
752  //jamming
753  if (has_jamming) {
754  str.str(clear_stringstream);
755  const bool cannot_jam = jams > type_.jamming();
756  if (cannot_jam) { // cannot jamm in this terrain
757  color = "red";
758  } else if (jams > views) {
759  color = "yellow";
760  } else if (jams == views) {
761  color = "white";
762  } else {
763  color = "green";
764  }
765  str << "<format>color=" << color << " text='";
766  // A 5 MP margin; if the jamming costs go above
767  // the unit's jamming + 5, we replace it with dashes.
768  if ( cannot_jam && jams > type_.jamming() + 5 ) {
770  } else {
771  str << jams;
772  }
773  str << "'</format>";
774 
775  push_tab_pair(row, str.str());
776  }
777 
778  table.push_back(row);
779  }
780  }
781 
782  ss << generate_table(table);
783  } else {
784  WRN_HP << "When building unit help topics, the display object was null and we couldn't get the terrain info we need.\n";
785  }
786  return ss.str();
787 }
788 
789 } // end namespace help
child_itors child_range(const std::string &key)
Definition: config.cpp:613
const t_string & help_topic_text() const
Definition: terrain.hpp:36
const std::string unit_prefix
Definition: help_impl.cpp:88
static lg::log_domain log_help("help")
const unit_type & get_gender_unit_type(std::string gender) const
Definition: types.cpp:439
const t_string & type_name() const
The name of the unit in the current language setting.
Definition: types.hpp:112
const std::vector< t_string > & abilities() const
Definition: types.hpp:166
void push_tab_pair(std::vector< std::pair< std::string, unsigned int > > &v, const std::string &s)
Definition: help_impl.cpp:1389
int movement() const
Definition: types.hpp:128
const std::vector< std::string > advances_from() const
Definition: types.cpp:649
int vision_cost(const t_translation::t_terrain &terrain, bool slowed=false) const
Returns the cost to see through the indicated terrain.
Definition: movetype.hpp:202
int pos
Definition: formula.cpp:800
virtual std::string operator()() const
const std::string unknown_unit_topic
Definition: help_impl.cpp:87
GLuint GLuint GLsizei GLenum type
Definition: glew.h:1221
t_translation::t_list::const_iterator t_it
logger & info()
Definition: log.cpp:91
const movetype & movement_type() const
Definition: types.hpp:150
const std::string & variation_name() const
Definition: types.hpp:136
const t_terrain MINUS
bool hide_in_editor() const
Definition: terrain.hpp:40
int jamming() const
Definition: types.hpp:132
const t_string & name() const
Definition: terrain.hpp:33
bool is_overlay() const
Definition: terrain.hpp:76
unit_type_data unit_types
Definition: types.cpp:1314
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:218
const std::string & image() const
Definition: types.hpp:138
static CVideo & get_singleton()
Definition: video.hpp:75
void push_header(std::vector< item > &row, const std::string &name) const
const t_translation::t_list & union_type() const
Definition: terrain.hpp:49
const t_terrain FOGGED
bool is_combined() const
Definition: terrain.hpp:77
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:55
const int normal_font_size
Definition: help_impl.cpp:80
void merge(const config &new_cfg, bool overwrite=true)
Merges the given config over the existing data.
Definition: movetype.cpp:754
The basic "size" of the unit - flying, small land, large land, etc.
Definition: movetype.hpp:28
unsigned image_width(const std::string &filename)
Definition: help_impl.cpp:1379
const std::string & editor_group() const
Definition: terrain.hpp:73
int level() const
Definition: types.hpp:126
const std::string terrain_prefix
Definition: help_impl.cpp:89
GLdouble GLdouble t
Definition: glew.h:1366
-file util.hpp
const t_terrain OFF_MAP_USER
bool capped(const t_translation::t_terrain &terrain) const
Returns whether there is a defense cap associated to this terrain.
Definition: movetype.hpp:134
const t_string & editor_name() const
Definition: terrain.hpp:34
int movement_cost(const t_translation::t_terrain &terrain, bool slowed=false) const
Returns the cost to move through the indicated terrain.
Definition: movetype.hpp:199
std::pair< const_child_iterator, const_child_iterator > const_child_itors
Definition: config.hpp:214
bool can_advance() const
Definition: types.hpp:173
GLdouble l
Definition: glew.h:6966
const std::string unicode_minus
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:225
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:82
const t_terrain PLUS
const std::string & flag_rgb() const
Definition: types.hpp:147
std::string generate_table(const table_spec &tab, const unsigned int spacing)
Definition: help_impl.cpp:1394
GLuint GLuint end
Definition: glew.h:1221
std::map< std::string, t_string > string_map
int gives_healing() const
Definition: terrain.hpp:61
const std::string unicode_en_dash
const std::string & big_profile() const
Definition: types.hpp:141
const std::string & editor_image() const
Definition: terrain.hpp:32
t_translation::t_terrain number() const
Definition: terrain.hpp:43
terrain_defense & get_defense()
Definition: movetype.hpp:184
std::vector< std::vector< std::pair< std::string, unsigned int > > > table_spec
Definition: help_impl.hpp:402
static const std::set< std::string > effects
The set of applicable effects for movement types.
Definition: movetype.hpp:233
GLuint start
Definition: glew.h:1221
static std::string print_behavior_description(t_it start, t_it end, const tdata_cache &tdata, bool first_level=true, bool begin_best=true)
std::set< t_translation::t_terrain > & encountered_terrains()
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:198
bool show_variations_in_help() const
Whether the unit type has at least one help-visible variation.
Definition: types.cpp:741
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:326
tdata_cache load_terrain_types_data()
Load the appropriate terrain types data to use.
Definition: help_impl.cpp:1443
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:135
bool is_village() const
Definition: terrain.hpp:62
const std::string & small_profile() const
Definition: types.hpp:140
#define WRN_HP
bool is_nonnull() const
Definition: terrain.hpp:51
GLuint const GLuint * names
Definition: glew.h:2552
GLuint color
Definition: glew.h:5801
utils::string_map damage_table() const
Returns a map from attack types to resistances.
Definition: movetype.hpp:219
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:215
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:47
int cost() const
Definition: types.hpp:134
const std::vector< std::string > & advances_to() const
Definition: types.hpp:97
static void print_trait_list(std::stringstream &ss, const std::vector< trait_data > &l)
const std::string unicode_figure_dash
const std::string variation_prefix
Definition: help_impl.cpp:93
std::pair< std::string, unsigned > item
int getx() const
Definition: video.cpp:472
std::string resistance_color(const int resistance)
Definition: helper.cpp:36
bool is_keep() const
Definition: terrain.hpp:64
const unit_type & get_variation(const std::string &id) const
Definition: types.cpp:457
config::const_child_itors possible_traits() const
Definition: types.hpp:182
std::string make_link(const std::string &text, const std::string &dst)
Definition: help_impl.hpp:375
int vision() const
Definition: types.hpp:129
size_t i
Definition: function.cpp:1057
std::vector< std::string > variations() const
Definition: types.cpp:726
unsigned screen_width
The screen resolution should be available for all widgets since their drawing method will depend on i...
Definition: settings.cpp:44
bool hide_help() const
Definition: types.cpp:561
virtual std::string operator()() const
const config & get_cfg() const
Definition: types.hpp:222
std::string escape(const std::string &s)
Prepend all chars with meaning inside attributes with a backslash.
Definition: help_impl.cpp:1437
GLuint const GLchar * name
Definition: glew.h:1782
int hitpoints() const
Definition: types.hpp:123
int experience_needed(bool with_acceleration=true) const
Definition: types.cpp:514
const t_terrain VOID_TERRAIN
GLenum GLenum GLvoid * row
Definition: glew.h:3805
std::string jump(const unsigned amount)
Definition: help_impl.hpp:388
const std::vector< t_string > & adv_abilities() const
Definition: types.hpp:170
bool has_terrain_defense_caps(const std::set< t_translation::t_terrain > &ts) const
Returns whether or not there are any terrain caps with respect to a set of terrains.
Definition: movetype.cpp:742
symbol_table string_table
Definition: language.cpp:65
bool has_jamming_data() const
Returns whether or not there are any jamming-specific costs.
Definition: movetype.hpp:227
std::vector< attack_type > attacks() const
Definition: types.cpp:484
Standard logging facilities (interface).
GLint * first
Definition: glew.h:1496
unsigned int num_traits() const
Definition: types.hpp:109
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:1155
std::vector< std::string > split(std::string const &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
Definition: help.cpp:57
const std::string & str() const
Definition: tstring.hpp:170
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
static std::string alignment_description(ALIGNMENT align, unit_race::GENDER gender=unit_race::MALE)
bool is_castle() const
Definition: terrain.hpp:63
GLdouble s
Definition: glew.h:1358
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:902
const t_string & income_description() const
Definition: terrain.hpp:68
GLenum GLsizei GLenum GLenum const GLvoid * table
Definition: glew.h:3780
GLsizei const GLcharARB ** string
Definition: glew.h:4503
int jamming_cost(const t_translation::t_terrain &terrain, bool slowed=false) const
Returns the cost to "jam" through the indicated terrain.
Definition: movetype.hpp:205
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: font.cpp:969
const std::string & icon_image() const
Definition: terrain.hpp:29
bool empty() const
Definition: tstring.hpp:166
std::pair< std::string, std::string > trait_data
const std::string & id() const
The id for this unit_type.
Definition: types.hpp:115
int defense_modifier(const t_translation::t_terrain &terrain) const
Returns the defensive value of the indicated terrain.
Definition: movetype.hpp:209
t_string unit_description() const
Definition: types.cpp:467
std::vector< t_terrain > t_list
Definition: translation.hpp:75