58 #define WRN_DP LOG_STREAM(warn, log_display) 61 #define WRN_HP LOG_STREAM(warn, log_help) 62 #define DBG_HP LOG_STREAM(debug, log_help) 101 const std::vector<std::string> toplevel_refs
103 if (std::find(toplevel_refs.begin(), toplevel_refs.end(), section_id)
104 != toplevel_refs.end()) {
111 const std::vector<std::string> sections_refd
113 if (std::find(sections_refd.begin(), sections_refd.end(), section_id)
114 != sections_refd.end()) {
123 if (
const config &toplevel = cfg.
child(
"toplevel"))
125 const std::vector<std::string> toplevel_refs
127 if (std::find(toplevel_refs.begin(), toplevel_refs.end(), topic_id)
128 != toplevel_refs.end()) {
135 const std::vector<std::string> topics_refd
137 if (std::find(topics_refd.begin(), topics_refd.end(), topic_id)
138 != topics_refd.end()) {
148 if (level > max_section_level) {
149 std::cerr <<
"Maximum section depth has been reached. Maybe circular dependency?" 152 else if (section_cfg !=
nullptr) {
154 std::string
id = level == 0 ?
"toplevel" : (*section_cfg)[
"id"].str();
157 std::stringstream ss;
158 ss <<
"Invalid ID, used for internal purpose: '" <<
id <<
"'";
162 std::string title = level == 0 ?
"" : (*section_cfg)[
"title"].str();
165 std::vector<std::string>::const_iterator it;
167 for (it = sections.begin(); it != sections.end(); ++it) {
175 std::stringstream ss;
176 ss <<
"Help-section '" << *it <<
"' referenced from '" 177 <<
id <<
"' but could not be found.";
183 if ((*section_cfg)[
"sort_sections"] ==
"yes") {
187 bool sort_topics =
false;
188 bool sort_generated =
true;
190 if ((*section_cfg)[
"sort_topics"] ==
"yes") {
192 sort_generated =
false;
193 }
else if ((*section_cfg)[
"sort_topics"] ==
"no") {
195 sort_generated =
false;
196 }
else if ((*section_cfg)[
"sort_topics"] ==
"generated") {
198 sort_generated =
true;
199 }
else if (!(*section_cfg)[
"sort_topics"].empty()) {
200 std::stringstream ss;
201 ss <<
"Invalid sort option: '" << (*section_cfg)[
"sort_topics"] <<
"'";
205 std::vector<topic> generated_topics =
209 std::vector<topic> topics;
212 for (it = topics_id.begin(); it != topics_id.end(); ++it) {
215 std::string text = topic_cfg[
"text"];
217 topic child_topic(topic_cfg[
"title"], topic_cfg[
"id"], text);
219 std::stringstream ss;
220 ss <<
"Invalid ID, used for internal purpose: '" <<
id <<
"'";
223 topics.push_back(child_topic);
226 std::stringstream ss;
227 ss <<
"Help-topic '" << *it <<
"' referenced from '" <<
id 228 <<
"' but could not be found." << std::endl;
237 std::merge(generated_topics.begin(),
238 generated_topics.end(),topics.begin(),topics.end()
243 topics.begin(), topics.end());
245 generated_topics.begin(), generated_topics.end());
253 if (cfg !=
nullptr) {
254 const config& toplevel_cfg = cfg->
child(
"toplevel");
262 std::vector<topic> res;
263 if (generator.empty()) {
267 if (generator ==
"abilities") {
269 }
else if (generator ==
"weapon_specials") {
271 }
else if (generator ==
"time_of_days") {
273 }
else if (generator ==
"traits") {
277 if (parts.size() > 1 && parts[0] ==
"units") {
279 }
else if (parts[0] ==
"era" && parts.size()>1) {
282 WRN_HP <<
"Found a topic generator that I didn't recognize: " << generator <<
"\n";
291 if (generator ==
"races") {
293 }
else if (generator ==
"terrains") {
295 }
else if (generator ==
"eras") {
296 DBG_HP <<
"Generating eras...\n";
300 if (parts.size() > 1 && parts[0] ==
"units") {
302 }
else if (generator.size() > 0) {
303 WRN_HP <<
"Found a section generator that I didn't recognize: " << generator <<
"\n";
310 std::string empty_string =
"";
311 if (generator.empty()) {
314 std::vector<std::string> parts =
utils::split(generator,
':');
315 if (parts.size() > 1 && parts[0] ==
"contents") {
316 if (parts[1] ==
"generated") {
345 return std::string(
"<format>color='") + (time_of_day_bonus > 0 ?
"green" : (time_of_day_bonus < 0 ?
"red" :
"white")) +
"' text='" + std::to_string(time_of_day_bonus) +
"'</format>";
350 std::vector<topic> topics;
351 std::stringstream toplevel;
354 toplevel <<
_(
"Only available during a scenario.");
355 topics.emplace_back(
_(
"Time of Day Schedule"),
"..schedule", toplevel.str());
362 const std::string
id =
"time_of_day_" + time.id;
363 const std::string
image =
"<img>src='" + time.image +
"'</img>";
364 const std::string image_lawful =
"<img>src='icons/alignments/alignment_lawful_30.png'</img>";
365 const std::string image_neutral =
"<img>src='icons/alignments/alignment_neutral_30.png'</img>";
366 const std::string image_chaotic =
"<img>src='icons/alignments/alignment_chaotic_30.png'</img>";
367 const std::string image_liminal =
"<img>src='icons/alignments/alignment_liminal_30.png'</img>";
368 std::stringstream text;
381 text << image <<
'\n' << time.description.str() <<
'\n' <<
386 '\n' <<
make_link(
_(
"Schedule"),
"..schedule");
388 topics.emplace_back(time.name.str(), id, text.str());
391 topics.emplace_back(
_(
"Time of Day Schedule"),
"..schedule", toplevel.str());
397 std::vector<topic> topics;
399 std::map<t_string, std::string> special_description;
400 std::map<t_string, std::set<std::string, string_less>> special_units;
402 for (
const unit_type_data::unit_type_map::value_type &type_mapping :
unit_types.
types())
412 std::vector<std::pair<t_string, t_string>> specials = atk.special_tooltips();
413 for ( std::size_t
i = 0;
i != specials.size(); ++
i )
415 special_description.emplace(specials[
i].first, specials[
i].second);
419 std::string type_name = type.
type_name();
422 std::string ref_id = section_prefix + unit_prefix + type.
id();
425 std::string link =
make_link(type_name, ref_id);
426 special_units[specials[
i].first].insert(link);
433 if(effect[
"apply_to"] ==
"new_attack" && effect.has_child(
"specials")) {
435 if(!spec.cfg[
"name"].empty()) {
436 special_description.emplace(spec.cfg[
"name"].t_str(), spec.cfg[
"description"].t_str());
439 std::string type_name = type.
type_name();
442 std::string ref_id = section_prefix + unit_prefix + type.
id();
445 std::string link =
make_link(type_name, ref_id);
446 special_units[spec.cfg[
"name"]].insert(link);
450 }
else if(effect[
"apply_to"] ==
"attack" && effect.has_child(
"set_specials")) {
451 for(
config::any_child spec : effect.child(
"set_specials").all_children_range()) {
452 if(!spec.cfg[
"name"].empty()) {
453 special_description.emplace(spec.cfg[
"name"].t_str(), spec.cfg[
"description"].t_str());
456 std::string type_name = type.
type_name();
459 std::string ref_id = section_prefix + unit_prefix + type.
id();
462 std::string link =
make_link(type_name, ref_id);
463 special_units[spec.cfg[
"name"]].insert(link);
473 s != special_description.end(); ++
s) {
475 std::string
id =
"weaponspecial_" +
s->first.base_str();
476 std::stringstream text;
478 text <<
"\n\n" <<
_(
"<header>text='Units with this special attack'</header>") <<
"\n";
479 std::set<std::string, string_less> &units = special_units[
s->first];
484 topics.emplace_back(
s->first,
id, text.str());
494 std::vector<topic> topics;
496 std::map<std::string, const unit_type::ability_metadata*> ability_topic_data;
497 std::map<std::string, std::set<std::string, string_less>> ability_units;
504 const std::string topic_ref = ability.id + ability.name.base_str();
506 ability_topic_data.emplace(topic_ref, &ability);
513 ability_units[topic_ref].insert(link);
521 const unit_type& type = type_mapping.second;
528 parse(type, ability);
532 parse(type, ability);
536 for(
const auto&
a : ability_topic_data) {
537 if (
a.second->name.empty()) {
540 std::ostringstream text;
541 text <<
a.second->description;
542 text <<
"\n\n" <<
_(
"<header>text='Units with this ability'</header>") <<
"\n";
544 for(
const auto& u : ability_units[
a.first]) {
548 topics.emplace_back(
a.second->name, ability_prefix +
a.first, text.str());
560 std::vector<topic> topics;
563 if(era && !era[
"hide_help"].to_bool()) {
566 std::vector<std::string> faction_links;
567 for (
const topic &
t : topics) {
571 std::stringstream text;
572 text <<
"<header>text='" <<
_(
"Era:") <<
" " << era[
"name"] <<
"'</header>" <<
"\n";
575 if (!description.
empty()) {
576 text << description.
t_str() <<
"\n";
580 text <<
"<header>text='" <<
_(
"Factions") <<
"'</header>" <<
"\n";
582 std::sort(faction_links.begin(), faction_links.end());
583 for (
const std::string &link : faction_links) {
587 topic era_topic(era[
"name"],
".." + era_prefix + era[
"id"].str(), text.str());
589 topics.push_back( era_topic );
596 std::vector<topic> topics;
598 const std::string&
id =
f[
"id"];
602 std::stringstream text;
605 if (!description.
empty()) {
606 text << description.
t_str() <<
"\n";
610 const std::vector<std::string> recruit_ids =
utils::split(
f[
"recruit"]);
611 std::set<std::string> races;
612 std::set<std::string> alignments;
614 for (
const std::string & u_id : recruit_ids) {
620 races.insert(
make_link(r->plural_name(), std::string(
"..") + race_prefix + r->id()));
626 if (!races.empty()) {
628 text <<
_(
"Races: ") << *(it++);
629 while(it != races.end()) {
630 text <<
", " << *(it++);
635 if (!alignments.empty()) {
637 text <<
_(
"Alignments: ") << *(it++);
638 while(it != alignments.end()) {
639 text <<
", " << *(it++);
644 text <<
"<header>text='" <<
_(
"Leaders") <<
"'</header>" <<
"\n";
645 const std::vector<std::string> leaders =
647 for (
const std::string &link : leaders) {
653 text <<
"<header>text='" <<
_(
"Recruits") <<
"'</header>" <<
"\n";
654 const std::vector<std::string> recruit_links =
656 for (
const std::string &link : recruit_links) {
660 const std::string
name =
f[
"name"];
661 const std::string ref_id = faction_prefix + era[
"id"] +
"_" + id;
662 topics.emplace_back(name, ref_id, text.str());
671 std::vector<topic> topics;
672 std::map<t_string, const config> trait_list;
675 const std::string trait_id = trait[
"id"];
676 trait_list.emplace(trait_id, trait);
680 for (
const unit_type_data::unit_type_map::value_type &
i :
unit_types.
types())
686 for (
const config & trait : traits) {
687 const std::string trait_id = trait[
"id"];
688 trait_list.emplace(trait_id, trait);
692 for (
const config & trait : r->additional_traits()) {
693 const std::string trait_id = trait[
"id"];
694 trait_list.emplace(trait_id, trait);
701 std::string
id =
"traits_" +
a->first;
702 const config trait =
a->second;
704 std::string
name = trait[
"male_name"].str();
705 if (name.empty()) name = trait[
"female_name"].str();
706 if (name.empty()) name = trait[
"name"].str();
707 if (name.empty())
continue;
709 std::stringstream text;
710 if (!trait[
"help_text"].empty()) {
711 text << trait[
"help_text"];
712 }
else if (!trait[
"description"].empty()) {
713 text << trait[
"description"];
715 text <<
_(
"No description available.");
718 if (trait[
"availability"] ==
"musthave") {
719 text <<
_(
"Availability: ") <<
_(
"Must-have") <<
"\n";
720 }
else if (trait[
"availability"] ==
"none") {
721 text <<
_(
"Availability: ") <<
_(
"Unavailable") <<
"\n";
723 topics.emplace_back(name,
id, text.str());
738 std::cerr <<
"Unknown unit type : " << type_id <<
"\n";
747 ref_id = section_prefix + unit_prefix + type->
id();
760 std::vector<std::string> links_list;
761 for (
const std::string &type_id : type_id_list) {
763 if (!unit_link.empty())
764 links_list.push_back(unit_link);
768 std::sort(links_list.begin(), links_list.end());
775 std::set<std::string, string_less> races;
776 std::set<std::string, string_less> visible_races;
778 for (
const unit_type_data::unit_type_map::value_type &
i :
unit_types.
types())
785 visible_races.insert(type.
race_id());
793 bool hidden = (visible_races.count(*it) == 0);
795 section_cfg[
"id"] =
hidden_symbol(hidden) + race_prefix + *it;
799 title = r->plural_name();
801 title =
_ (
"race^Miscellaneous");
803 section_cfg[
"title"] = title;
805 section_cfg[
"sections_generator"] =
"units:" + *it;
806 section_cfg[
"generator"] =
"units:" + *it;
816 if (
era[
"hide_help"].to_bool()) {
820 DBG_HP <<
"Adding help section: " <<
era[
"id"].str() <<
"\n";
824 section_cfg[
"id"] = era_prefix +
era[
"id"].str();
825 section_cfg[
"title"] =
era[
"name"];
827 section_cfg[
"generator"] =
"era:" +
era[
"id"].str();
841 WRN_HP <<
"When building terrain help sections, couldn't acquire terrain types data, aborting.\n";
845 std::map<std::string, section> base_map;
862 terrain_topic.
text = std::make_shared<terrain_topic_generator>(
info);
867 const terrain_type& base_info = tdata->get_terrain_info(base);
872 section& base_section = base_map[base_info.
id()];
874 base_section.
id = terrain_prefix + base_info.
id();
877 if (base_info.
id() == info.
id())
878 terrain_topic.
id =
".." + terrain_prefix + info.
id();
879 base_section.
topics.push_back(terrain_topic);
883 for (
const auto& base : base_map) {
890 for (
const unit_type_data::unit_type_map::value_type &
i :
unit_types.
types()) {
900 for (
const std::string &variation_id : type.
variations()) {
904 const std::string var_ref =
hidden_symbol(var_type.
hide_help()) + variation_prefix + var_type.
id() +
"_" + variation_id;
906 topic var_topic(topic_name, var_ref,
"");
907 var_topic.
text = std::make_shared<unit_topic_generator>(var_type, variation_id);
908 base_unit.
topics.push_back(var_topic);
911 const std::string type_name = type.
type_name();
914 base_unit.
id = ref_id;
915 base_unit.
title = type_name;
923 std::vector<topic> topics;
924 std::set<std::string, string_less> race_units;
925 std::set<std::string, string_less> race_topics;
926 std::set<std::string> alignments;
928 for (
const unit_type_data::unit_type_map::value_type &
i :
unit_types.
types())
943 topic unit_topic(type_name, ref_id,
"");
944 unit_topic.
text = std::make_shared<unit_topic_generator>(
type);
945 topics.push_back(unit_topic);
950 std::string link =
make_link(type_name, ref_id);
951 race_units.insert(link);
958 std::string race_id =
"..race_"+race;
959 std::string race_name;
960 std::string race_description;
961 std::string race_help_taxonomy;
963 race_name = r->plural_name();
964 race_description = r->description();
965 race_help_taxonomy = r->help_taxonomy();
967 for (
const config &additional_topic : r->additional_topics())
969 std::string
id = additional_topic[
"id"];
970 std::string title = additional_topic[
"title"];
971 std::string text = additional_topic[
"text"];
973 topics.emplace_back(title,
id,text);
975 race_topics.insert(link);
978 race_name =
_ (
"race^Miscellaneous");
983 std::map<std::string, t_string> subgroups;
985 if (r.second.help_taxonomy() == race) {
986 if (!r.second.plural_name().empty())
987 subgroups[r.first] = r.second.plural_name();
989 subgroups[r.first] = r.first;
993 std::stringstream text;
995 if (!race_description.empty()) {
996 text << race_description <<
"\n\n";
999 if (!alignments.empty()) {
1001 text << (alignments.size() > 1 ?
_(
"Alignments: ") :
_(
"Alignment: ")) << *(it++);
1002 while(it != alignments.end()) {
1003 text <<
", " << *(it++);
1008 if (!race_help_taxonomy.empty()) {
1010 symbols[
"topic_id"] =
"..race_"+race_help_taxonomy;
1012 symbols[
"help_taxonomy"] = r->plural_name();
1016 symbols[
"help_taxonomy"] = race_help_taxonomy;
1020 text <<
VGETTEXT(
"This is a group of units, all of whom are <ref>dst='$topic_id' text='$help_taxonomy'</ref>", symbols) <<
"\n\n";
1023 if (!subgroups.empty()) {
1024 if (!race_help_taxonomy.empty()) {
1025 text <<
_(
"<header>text='Subgroups of units within this group'</header>") <<
"\n";
1027 text <<
_(
"<header>text='Groups of units within this race'</header>") <<
"\n";
1029 for (
const auto &sg : subgroups) {
1035 if (!race_help_taxonomy.empty()) {
1036 text <<
_(
"<header>text='Units of this group'</header>") <<
"\n";
1038 text <<
_(
"<header>text='Units of this race'</header>") <<
"\n";
1040 for (
const auto &u : race_units) {
1044 topics.emplace_back(race_name, race_id, text.str());
1060 if (encountered_units.find(type.
id()) != encountered_units.end()) {
1065 if (type.
id() ==
"Fog Clearer") {
1074 const config& section_cfg = help_cfg->
find_child(
"section",
"id", section_name);
1076 return std::string();
1079 std::ostringstream res;
1084 typedef std::pair<std::string,std::string> link;
1085 std::vector<link> topics_links;
1089 for (t = topics.begin(); t != topics.end(); ++
t) {
1091 std::string
id = topic_cfg[
"id"];
1093 topics_links.emplace_back(topic_cfg[
"title"],
id);
1097 if (section_cfg[
"sort_topics"] ==
"yes") {
1098 std::sort(topics_links.begin(),topics_links.end());
1102 for (l = topics_links.begin(); l != topics_links.end(); ++l) {
1103 std::string link =
make_link(l->first, l->second);
1112 std::stringstream res;
1121 for (
auto &
t : topics) {
1142 return sec.
id == id;
1152 sections.emplace_back(s);
1163 topic_list::const_iterator tit =
1165 if (tit != sec.
topics.end()) {
1184 for (
const auto &subsection : sec.
sections) {
1200 std::vector<std::string> res;
1201 bool last_char_escape =
false;
1202 const char escape_char =
'\\';
1203 std::stringstream ss;
1206 for (pos = 0; pos < text.size(); ++pos) {
1207 const char c = text[pos];
1208 if (c == escape_char && !last_char_escape) {
1209 last_char_escape =
true;
1212 if (state ==
OTHER) {
1214 if (last_char_escape) {
1218 res.push_back(ss.str());
1220 state = ELEMENT_NAME;
1227 else if (state == ELEMENT_NAME) {
1229 std::string
msg =
"Erroneous / in element name.";
1232 else if (c ==
'>') {
1234 std::stringstream
s;
1235 const std::string element_name = ss.str();
1237 s <<
"</" << element_name <<
">";
1238 const std::string end_element_name = s.str();
1239 std::size_t end_pos = text.find(end_element_name, pos);
1240 if (end_pos == std::string::npos) {
1241 std::stringstream
msg;
1242 msg <<
"Unterminated element: " << element_name;
1246 const std::string contents = text.substr(pos + 1, end_pos - pos - 1);
1247 const std::string element =
convert_to_wml(element_name, contents);
1248 res.push_back(element);
1249 pos = end_pos + end_element_name.size() - 1;
1256 last_char_escape =
false;
1259 if (state == ELEMENT_NAME) {
1260 std::stringstream
msg;
1261 msg <<
"Element '" << ss.str() <<
"' continues through end of string.";
1264 if (!ss.str().empty()) {
1266 res.push_back(ss.str());
1271 std::string
convert_to_wml(
const std::string &element_name,
const std::string &contents)
1273 std::stringstream ss;
1274 bool in_quotes =
false;
1275 bool last_char_escape =
false;
1276 const char escape_char =
'\\';
1277 std::vector<std::string> attributes;
1282 for (std::size_t pos = 0; pos < contents.size(); ++pos) {
1283 const char c = contents[pos];
1284 if (c == escape_char && !last_char_escape) {
1285 last_char_escape =
true;
1288 if (c ==
'\'' && !last_char_escape) {
1290 in_quotes = !in_quotes;
1292 else if ((c ==
' ' || c ==
'\n') && !last_char_escape && !in_quotes) {
1294 attributes.push_back(ss.str());
1300 last_char_escape =
false;
1304 std::stringstream
msg;
1305 msg <<
"Unterminated single quote after: '" << ss.str() <<
"'";
1308 if (!ss.str().empty()) {
1309 attributes.push_back(ss.str());
1313 ss <<
"[" << element_name <<
"]\n";
1314 for (std::vector<std::string>::const_iterator it = attributes.begin();
1315 it != attributes.end(); ++it) {
1318 ss <<
"[/" << element_name <<
"]\n";
1324 if (cmp_str ==
"green") {
1327 if (cmp_str ==
"red") {
1330 if (cmp_str ==
"black") {
1333 if (cmp_str ==
"yellow") {
1336 if (cmp_str ==
"white") {
1340 if (*cmp_str.c_str() ==
'#' && cmp_str.size() == 7) {
1347 const unsigned width)
1349 std::vector<std::string> res;
1352 res.push_back(first_line);
1353 if(s.size() > first_line.size()) {
1354 res.push_back(s.substr(first_line.size()));
1367 if (text.length() > 0 && text[0] ==
' ') {
1368 return text.substr(1);
1375 std::size_t first_word_start = s.find_first_not_of(
' ');
1376 if (first_word_start == std::string::npos) {
1379 std::size_t first_word_end = s.find_first_of(
" \n", first_word_start);
1380 if( first_word_end == first_word_start ) {
1382 first_word_end = first_word_start+1;
1386 std::string re = s.substr(0, first_word_end);
1392 char32_t firstchar = *ch;
1401 default_toplevel.
clear();
1402 hidden_sections.
clear();
1403 if (game_cfg !=
nullptr) {
1414 std::stringstream ss;
1417 const std::string
id =
section[
"id"];
1423 if (!ss.str().empty()) {
1430 hidden_toplevel[
"sections"] = ss.str();
1434 const std::string
id =
topic[
"id"];
1435 if (
find_topic(default_toplevel,
id) ==
nullptr) {
1437 if (!ss.str().empty()) {
1444 hidden_toplevel[
"topics"] = ss.str();
1445 config hidden_cfg = *help_config;
1448 hidden_cfg.
add_child(
"toplevel", std::move(hidden_toplevel));
1452 std::stringstream
msg;
1453 msg <<
"Parse error when parsing help text: '" << e.
message <<
"'";
1454 std::cerr << msg.str() << std::endl;
1461 return (hidden ?
"." :
"");
1465 return (
id.empty() ||
id[0] !=
'.');
1474 if (
id ==
"toplevel") {
1480 if (
id.
compare(0, 8,
"ability_") == 0) {
1483 if (
id.
compare(0, 14,
"weaponspecial_") == 0) {
1486 if (
id ==
"hidden") {
1498 if (surf !=
nullptr) {
1504 void push_tab_pair(std::vector<help::item> &v,
const std::string &
s,
const std::optional<std::string> &
image,
unsigned padding)
1510 padding = (width ? padding : 0);
1512 item.first =
"<img>src='" + *image +
"'</img>" + (padding ?
jump(padding) :
"") + s;
1513 item.second += width + padding;
1515 v.emplace_back(item);
1520 table_spec::const_iterator row_it;
1521 std::vector<std::pair<std::string, unsigned>>::const_iterator col_it;
1522 unsigned int num_cols = 0;
1523 for (row_it = tab.begin(); row_it != tab.end(); ++row_it) {
1524 if (row_it->size() > num_cols) {
1525 num_cols = row_it->size();
1528 std::vector<unsigned int> col_widths(num_cols, 0);
1530 for (row_it = tab.begin(); row_it != tab.end(); ++row_it) {
1531 unsigned int col = 0;
1532 for (col_it = row_it->begin(); col_it != row_it->end(); ++col_it) {
1533 if (col_widths[col] < col_it->second + spacing) {
1534 col_widths[col] = col_it->second + spacing;
1539 std::vector<unsigned int> col_starts(num_cols);
1541 for (
unsigned int i = 0;
i < num_cols; ++
i) {
1542 unsigned int this_col_start = 0;
1543 for (
unsigned int j = 0; j <
i; ++j) {
1544 this_col_start += col_widths[j];
1546 col_starts[
i] = this_col_start;
1548 std::stringstream ss;
1549 for (row_it = tab.begin(); row_it != tab.end(); ++row_it) {
1550 unsigned int col = 0;
1551 for (col_it = row_it->begin(); col_it != row_it->end(); ++col_it) {
1552 ss <<
jump_to(col_starts[col]) << col_it->first;
std::string jump_to(const unsigned pos)
surface get_image(const image::locator &i_locator, TYPE type)
Caches and returns an image.
bool empty() const
Tests for an attribute that either was never set or was set to "".
const std::string ability_prefix
section parse_config(const config *cfg)
Parse a help config, return the top level section.
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
::tod_manager * tod_manager
Ignore this unit for documentation purposes.
std::string make_unit_link(const std::string &type_id)
return a hyperlink with the unit's name and pointing to the unit page return empty string if this uni...
const std::string open_section_img
int get_max_liminal_bonus() const
static display * get_singleton()
Returns the display object if a display object exists.
std::vector< topic > generate_trait_topics(const bool sort_generated)
const std::string unit_prefix
void push_tab_pair(std::vector< help::item > &v, const std::string &s, const std::optional< std::string > &image, unsigned padding)
std::vector< topic > generate_unit_topics(const bool sort_generated, const std::string &race)
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.
std::map< std::string, t_string > string_map
const std::string era_prefix
const std::string topic_img
void clear_children(T... keys)
config_array_view traits() const
const std::string closed_section_img
std::string remove_first_space(const std::string &text)
bool is_scope_active(scope s)
A section contains topics and sections along with title and ID.
Various functions that implement attacks and attack calculations.
config & find_child(config_key_type key, const std::string &name, const std::string &value)
Returns the first child of tag key with a name attribute containing value.
const std::string unknown_unit_topic
Variant for storing WML attributes.
const std::string race_prefix
std::string generate_topic_text(const std::string &generator, const config *help_cfg, const section &sec, const std::vector< topic > &generated_topics)
config_array_view child_range(config_key_type key) const
int compare(const std::string &s1, const std::string &s2)
Case-sensitive lexicographical comparison.
boost::tribool last_debug_state
ucs4_convert_impl::enableif< TD, typename TS::value_type >::type unicode_cast(const TS &source)
child_itors child_range(config_key_type key)
REMOVE_EMPTY: remove empty elements.
Thrown when the help system fails to parse something.
bool operator<(const section &) const
Comparison on the ID.
std::vector< std::string > empty_string_vector
Although the unit itself is hidden, traits reachable via this unit are not hidden.
std::string word_wrap_text(const std::string &unwrapped_text, int font_size, int max_width, int max_height, int max_lines, bool partial_line)
Wrap text.
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
unit_type_data unit_types
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
int generic_combat_modifier(int lawful_bonus, unit_type::ALIGNMENT alignment, bool is_fearless, int max_liminal_bonus)
Returns the amount that a unit's damage should be multiplied by due to a given lawful_bonus.
const race_map & races() const
const int normal_font_size
unsigned image_width(const std::string &filename)
static std::string _(const char *str)
const std::string terrain_prefix
int last_num_encountered_terrains
color_t string_to_color(const std::string &cmp_str)
Return the color the string represents.
const unit_type_map & types() const
bool is_visible_id(const std::string &id)
virtual const gamemap & map() const =0
bool hide_help() const
For instances created from a [terrain_type] tag, the value in the tag (with default false)...
std::vector< topic > generate_weapon_special_topics(const bool sort_generated)
std::vector< std::string > make_unit_links_list(const std::vector< std::string > &type_id_list, bool ordered)
return a list of hyperlinks to unit's pages (ordered or not)
A single unit type that the player may recruit.
bool is_nonnull() const
True if this object represents some sentinel values.
void generate_sections(const config *help_cfg, const std::string &generator, section &sec, int level)
Dispatch generators to their appropriate functions.
help::section hidden_sections
const std::shared_ptr< terrain_type_data > & terrain_types() const
const section * find_section(const section &sec, const std::string &id)
Search for the section with the specified identifier in the section and its subsections.
Object which defines a time of day with associated bonuses, image, sounds etc.
To be used as a function object to locate sections and topics with a specified ID.
static game_config_manager * get()
const t_string & editor_name() const
const color_t BIGMAP_COLOR
The text displayed in a topic.
std::string generate_table(const table_spec &tab, const unsigned int spacing)
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...
std::vector< std::vector< help::item > > table_spec
std::string hidden_symbol(bool hidden)
std::shared_ptr< terrain_type_data > load_terrain_types_data()
Load the appropriate terrain types data to use.
const t_string & type_name() const
The name of the unit in the current language setting.
std::vector< topic > generate_era_topics(const bool sort_generated, const std::string &era_id)
std::vector< topic > generate_faction_topics(const config &era, const bool sort_generated)
std::string time_of_day_bonus_colored(const int time_of_day_bonus)
std::set< t_translation::terrain_code > & encountered_terrains()
const std::vector< ability_metadata > & abilities_metadata() const
std::string race_id() const
Returns the ID of this type's race without the need to build the type.
const std::string & id() const
The id for this unit_type.
map_display and display: classes which take care of displaying the map and game-data on the screen...
std::vector< std::string > parsed_text_
const std::shared_ptr< terrain_type_data > & tdata() const
static lg::log_domain log_help("help")
const color_t YELLOW_COLOR
bool show_all_units_in_help()
const config & child_or_empty(config_key_type key) const
const color_t NORMAL_COLOR
std::shared_ptr< topic_generator > generator_
Generic locator abstracting the location of an image.
void parse_config_internal(const config *help_cfg, const config *section_cfg, section &sec, int level)
Recursive function used by parse_config.
static color_t from_argb_bytes(uint32_t c)
Creates a new color_t object from a uint32_t variable.
std::string escape(const std::string &str, const char *special_chars)
Prepends a configurable set of characters with a backslash.
boost::iterator_range< const_child_iterator > const_child_itors
bool section_is_referenced(const std::string §ion_id, const config &cfg)
Return true if the section with id section_id is referenced from another section in the config...
bool topic_is_referenced(const std::string &topic_id, const config &cfg)
Return true if the topic with id topic_id is referenced from another section in the config...
void generate_unit_sections(const config *, section &sec, int, const bool, const std::string &race)
To be used as a function object when sorting section lists on the title.
config::const_child_itors modification_advancements() const
Returns two iterators pointing to a range of AMLA configs.
const unit_race * find_race(const std::string &) const
const color_t BLACK_COLOR
const std::string variation_prefix
std::vector< std::string > variations() const
Thrown by operations encountering invalid UTF-8 data.
void generate_terrain_sections(section &sec, int)
int last_num_encountered_units
std::string make_link(const std::string &text, const std::string &dst)
std::string convert_to_wml(const std::string &element_name, const std::string &contents)
Convert the contents to wml attributes, surrounded within [element_name]...[/element_name].
static map_location::DIRECTION s
bool show_variations_in_help() const
Whether the unit type has at least one help-visible variation.
const std::vector< time_of_day > & times(const map_location &loc=map_location::null_location()) const
std::vector< std::string > quoted_split(const std::string &val, char c, int flags, char quote)
This function is identical to split(), except it does not split when it otherwise would if the previo...
const display_context & get_disp_context() const
const game_config_view * game_cfg
std::set< std::string > & encountered_units()
bool operator<(const topic &) const
Comparison on the ID.
void generate_races_sections(const config *help_cfg, section &sec, int level)
const std::string unicode_bullet
static int sort(lua_State *L)
static iterator_base end(const string_type &str)
const std::vector< ability_metadata > & adv_abilities_metadata() const
Some extra abilities that may be gained through AMLA advancements.
rng * generator
This generator is automatically synced during synced context.
std::string escape(const std::string &s)
Prepend all chars with meaning inside attributes with a backslash.
std::vector< std::string > parse_text(const std::string &text)
Parse a text string.
config & add_child(config_key_type key)
const config & find_child(config_key_type key, const std::string &name, const std::string &value) const
const unsigned max_history
std::vector< topic > generate_time_of_day_topics(const bool)
config::const_child_itors possible_traits() const
static lg::log_domain log_display("display")
std::string generate_contents_links(const std::string §ion_name, config const *help_cfg)
const std::string faction_prefix
std::string jump(const unsigned amount)
const std::string default_show_topic
const unit_type & get_variation(const std::string &id) const
void generate_contents()
Generate the help contents from the configurations given to the manager.
std::vector< std::string > split(const config_attribute_value &val)
std::vector< topic > generate_topics(const bool sort_generated, const std::string &generator)
bool operator==(const topic &) const
Two topics are equal if their IDs are equal.
const topic * find_topic(const section &sec, const std::string &id)
Search for the topic with the specified identifier in the section and its subsections.
const t_string & variation_name() const
const std::vector< std::string > & parsed_text() const
Functions to load and save images from/to disk.
Standard logging facilities (interface).
const std::string & id() const
std::vector< terrain_code > ter_list
A topic contains a title, an id and some text.
const_attack_itors attacks() const
bool is_valid_id(const std::string &id)
Return true if the id is valid for user defined topics and sections.
A config object defines a single node in a WML file, with access to child nodes.
static std::string alignment_description(ALIGNMENT align, unit_race::GENDER gender=unit_race::MALE)
const std::string & str() const
std::string get_first_word(const std::string &s)
Return the first word in s, not removing any spaces in the start of it.
ALIGNMENT alignment() const
bool is_cjk_char(const char32_t ch)
Determine if a char32_t is a CJK character.
void add_section(const section &s)
Allocate memory for and add the section.
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. ...
std::vector< topic > generate_ability_topics(const bool sort_generated)
void generate_era_sections(const config *help_cfg, section &sec, int level)
Defines the MAKE_ENUM macro.
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.
std::string::const_iterator iterator
const int max_section_level
topic_text & operator=(topic_text &&t)=default
To be used as a function object when sorting topic lists on the title.
std::vector< std::string > split_in_width(const std::string &s, const int font_size, const unsigned width)
Make a best effort to word wrap s.
std::string debug() const
std::pair< std::string, unsigned > item
bool operator==(const section &) const
Two sections are equal if their IDs are equal.
help::section default_toplevel