46 #include <boost/algorithm/string.hpp>
54 #define WRN_HP LOG_STREAM(warn, log_help)
55 #define DBG_HP LOG_STREAM(debug, log_help)
111 PLAIN_LOG <<
"Maximum section depth has been reached. Maybe circular dependency?";
115 std::string
id =
level == 0 ?
"toplevel" : section_cfg[
"id"].str();
118 std::stringstream ss;
119 ss <<
"Invalid ID, used for internal purpose: '" <<
id <<
"'";
123 std::string title =
level == 0 ?
"" : section_cfg[
"title"].str();
128 for(
const std::string& sec_id : sections) {
129 if(
auto child_cfg = help_cfg.
find_child(
"section",
"id", sec_id)) {
132 std::stringstream ss;
133 ss <<
"Help-section '" << sec_id <<
"' referenced from '"
134 <<
id <<
"' but could not be found.";
140 if(section_cfg[
"sort_sections"] ==
"yes") {
144 bool sort_topics =
false;
145 bool sort_generated =
true;
147 if(section_cfg[
"sort_topics"] ==
"yes") {
149 sort_generated =
false;
150 }
else if(section_cfg[
"sort_topics"] ==
"no") {
152 sort_generated =
false;
153 }
else if(section_cfg[
"sort_topics"] ==
"generated") {
155 sort_generated =
true;
156 }
else if(!section_cfg[
"sort_topics"].empty()) {
157 std::stringstream ss;
158 ss <<
"Invalid sort option: '" << section_cfg[
"sort_topics"] <<
"'";
162 std::vector<topic> generated_topics =
generate_topics(sort_generated, section_cfg[
"generator"]);
163 std::vector<topic> topics;
167 if(
auto topic_cfg = help_cfg.
find_child(
"topic",
"id", topic_id)) {
168 std::string text = topic_cfg[
"text"];
170 topic child_topic(topic_cfg[
"title"], topic_cfg[
"id"], text);
172 std::stringstream ss;
173 ss <<
"Invalid ID, used for internal purpose: '" <<
id <<
"'";
176 topics.push_back(child_topic);
178 std::stringstream ss;
179 ss <<
"Help-topic '" << topic_id <<
"' referenced from '" <<
id
180 <<
"' but could not be found." << std::endl;
186 std::sort(topics.begin(),topics.end(),
title_less());
187 std::sort(generated_topics.begin(),
189 std::merge(generated_topics.begin(),
190 generated_topics.end(),topics.begin(),topics.end()
194 topics.begin(), topics.end());
196 generated_topics.begin(), generated_topics.end());
212 std::vector<topic> res;
219 }
else if(
generator ==
"weapon_specials") {
227 if(parts.size() > 1 && parts[0] ==
"units") {
229 }
else if(parts[0] ==
"era" && parts.size() > 1) {
232 WRN_HP <<
"Found a topic generator that I didn't recognize: " <<
generator;
246 DBG_HP <<
"Generating eras...";
250 if(parts.size() > 1 && parts[0] ==
"units") {
253 WRN_HP <<
"Found a section generator that I didn't recognize: " <<
generator;
260 std::string empty_string =
"";
265 if(parts.size() > 1 && parts[0] ==
"contents") {
266 if(parts[1] ==
"generated") {
288 return markup::span_color((time_of_day_bonus > 0 ?
"green" : (time_of_day_bonus < 0 ?
"red" :
"white")), time_of_day_bonus);
293 std::vector<topic> topics;
294 std::stringstream toplevel;
297 toplevel <<
_(
"Only available during a scenario.");
298 topics.emplace_back(
_(
"Time of Day Schedule"),
"..schedule", toplevel.str());
305 const std::string
id =
"time_of_day_" + time.id;
307 const std::string image_lawful =
markup::img(
"icons/alignments/alignment_lawful_30.png");
308 const std::string image_neutral =
markup::img(
"icons/alignments/alignment_neutral_30.png");
309 const std::string image_chaotic =
markup::img(
"icons/alignments/alignment_chaotic_30.png");
310 const std::string image_liminal =
markup::img(
"icons/alignments/alignment_liminal_30.png");
311 std::stringstream text, row_ss;
326 text <<
image <<
'\n'
327 << time.description.str() <<
'\n'
334 topics.emplace_back(time.name.str(),
id, text.str());
337 topics.emplace_back(
_(
"Time of Day Schedule"),
"..schedule",
markup::tag(
"table", toplevel.str()));
343 std::vector<topic> topics;
348 auto special_description = std::set<unit_ability_t::tooltip_info, decltype(comp)>(comp);
351 std::map<std::string, std::string> specials_check_map;
353 std::map<std::string, std::set<std::string, string_less>> special_units;
362 for(
auto& tt_info : atk.special_tooltips()) {
363 special_description.emplace(tt_info);
364 const auto& [itor, is_added] = specials_check_map.emplace(tt_info.help_topic_id, tt_info.name.base_str());
365 if(!is_added && itor->second != tt_info.name.base_str()) {
366 WRN_HP <<
"Duplicate weapon special with help id ‘" << tt_info.help_topic_id <<
"’ but different name ‘"
367 << tt_info.name.base_str() <<
"’ != ‘" << itor->second <<
"’ detected, help page not added again.";
370 if(!
type.hide_help()) {
376 for(
const config& adv :
type.modification_advancements()) {
378 if(effect[
"apply_to"] ==
"new_attack" && effect.has_child(
"specials")) {
379 for(
const auto [
_, special] : effect.mandatory_child(
"specials").all_children_view()) {
380 if(!special[
"name"].empty()) {
382 const t_string& name = special[
"name"].t_str();
385 special_description.insert({ name, special[
"description"].t_str(), topic_id });
387 const auto& [itor, is_added] = specials_check_map.emplace(topic_id, name.
base_str());
388 if(!is_added && itor->second != name.
base_str()) {
389 WRN_HP <<
"Duplicate weapon special with help id ‘" << topic_id <<
"’ but different name ‘"
390 << name.
base_str() <<
"’ != ‘" << itor->second <<
"’ detected, help page not added again.";
393 if(!
type.hide_help()) {
398 }
else if(effect[
"apply_to"] ==
"attack" && effect.has_child(
"set_specials")) {
399 for(
const auto [
_, special] : effect.mandatory_child(
"set_specials").all_children_view()) {
400 if(!special[
"name"].empty()) {
402 const t_string& name = special[
"name"].t_str();
405 special_description.insert({ name, special[
"description"].t_str(), topic_id });
407 const auto& [itor, is_added] = specials_check_map.emplace(topic_id, name.
base_str());
408 if(!is_added && itor->second != name.
base_str()) {
409 WRN_HP <<
"Duplicate weapon special with help id ‘" << topic_id <<
"’ but different name ‘"
410 << name.
base_str() <<
"’ != ‘" << itor->second <<
"’ detected, help page not added again.";
413 if(!
type.hide_help()) {
423 for(
const auto& [name, description, help_topic_id] : special_description) {
425 std::stringstream text;
427 text <<
"\n\n" <<
markup::tag(
"header",
_(
"Units with this special attack")) <<
"\n";
428 for(
const std::string& type_link : special_units[help_topic_id]) {
432 topics.emplace_back(name,
id, text.str());
436 std::sort(topics.begin(), topics.end(),
title_less());
442 std::vector<topic> topics;
444 std::map<std::string, const unit_type::ability_metadata&> ability_topic_data;
445 std::map<std::string, std::set<std::string, string_less>> ability_units;
448 const auto& [itor, is_added] = ability_topic_data.emplace(ability.help_topic_id, ability);
449 if(!is_added && itor->second.name.base_str() != ability.name.base_str()) {
450 WRN_HP <<
"Duplicate ability with help id ‘" << ability.help_topic_id <<
"’ but different name ‘"
451 << ability.name.base_str() <<
"’ != ‘" << itor->second.name.base_str() <<
"’ detected, help page not added again.";
454 if(!
type.hide_help()) {
468 parse(
type, ability);
472 parse(
type, ability);
476 for(
const auto& [help_topic_id, ability] : ability_topic_data) {
477 if(ability.name.empty()) {
480 std::ostringstream text;
481 text << ability.description;
482 text <<
"\n\n" <<
markup::tag(
"header",
_(
"Units with this ability")) <<
"\n";
484 for(
const auto& link : ability_units[help_topic_id]) {
488 topics.emplace_back(ability.name,
ability_prefix + help_topic_id, text.str());
492 std::sort(topics.begin(), topics.end(),
title_less());
500 std::vector<topic> topics;
503 if(era && !era[
"hide_help"].to_bool()) {
506 std::vector<std::string> faction_links;
507 for(
const topic&
t : topics) {
511 std::stringstream text;
513 if(!description.
empty()) {
514 text << description.
t_str() <<
"\n";
520 std::sort(faction_links.begin(), faction_links.end());
521 for(
const std::string& link : faction_links) {
525 topics.emplace_back(era[
"name"],
".." +
era_prefix + era[
"id"].str(), text.str());
532 std::vector<topic> topics;
533 std::set<std::string> faction_help_ids;
536 const std::string&
id =
f[
"id"];
540 std::stringstream text;
543 if(!description.
empty()) {
544 text << description.
t_str() <<
"\n";
548 const std::vector<std::string> recruit_ids =
utils::split(
f[
"recruit"]);
549 std::set<std::string> races;
550 std::set<std::string> alignments;
552 for(
const std::string& u_id : recruit_ids) {
566 text <<
_(
"Races: ") << *(it++);
567 while(it != races.end()) {
568 text <<
", " << *(it++);
573 if(!alignments.empty()) {
575 text <<
_(
"Alignments: ") << *(it++);
576 while(it != alignments.end()) {
577 text <<
", " << *(it++);
583 const std::vector<std::string> leaders =
585 for(
const std::string& link : leaders) {
592 const std::vector<std::string> recruit_links =
594 for(
const std::string& link : recruit_links) {
598 const std::string name =
f[
"name"];
600 const bool is_added = faction_help_ids.insert(ref_id).second;
602 WRN_HP <<
"Duplicate faction with id ‘" <<
id <<
"’, (help id ‘" << ref_id <<
"’) detected.";
604 topics.emplace_back(name, ref_id, text.str());
607 std::sort(topics.begin(), topics.end(),
title_less());
612 const unsigned PAGE_LIMIT = 20;
616 void add_remaining_pages(
617 std::vector<topic>& topics,
618 const std::string& topic_name,
619 const std::string& topic_id,
620 const std::string& suffix,
621 const std::set<std::string, string_less>& list)
623 const size_t rem = list.size() % PAGE_LIMIT;
624 const size_t page_count = list.size() / PAGE_LIMIT + (rem != 0 ? 1 : 0);
625 auto it = std::next(list.begin(), PAGE_LIMIT);
627 for(
size_t page_num = 2; page_num <= page_count; page_num++) {
628 std::stringstream text;
631 std::string prev_id = topic_id;
633 prev_id =
"." + topic_id + suffix +
"_" + std::to_string(page_num - 1);
639 for(
size_t row = 0; row < PAGE_LIMIT; row++) {
640 if(it != list.end()) {
647 if(page_num != page_count) {
649 <<
markup::make_link(
_(
"Next") +
" >>",
"." + topic_id + suffix +
"_" + std::to_string(page_num + 1))
653 std::string new_topic_name =
formatter() << topic_name <<
" (" << page_num <<
"/" << page_count <<
")";
654 topics.emplace_back(new_topic_name,
"." + topic_id + suffix +
"_" + std::to_string(page_num), text.str());
665 std::map<std::string, const config> trait_list;
667 std::set<std::string, string_less> global_traits;
670 std::map<std::string, std::set<std::string, string_less>> trait_units;
673 std::map<std::string, std::set<std::string, string_less>> trait_races;
677 trait_list.emplace(trait[
"id"], trait);
678 if(!global_traits.insert(trait[
"id"]).second) {
679 WRN_HP <<
"Duplicate global trait ‘" << trait[
"id"] <<
"’ detected, help page not added again.";
684 std::set<std::string> races;
688 races.insert(
type.race_id());
697 for(
const auto& race_id : races) {
699 for(
const config& trait : r->additional_traits()) {
701 trait_list.emplace(trait[
"id"], trait);
702 if(!trait_races[trait[
"id"]].
insert(race_id).second) {
703 WRN_HP <<
"Duplicate trait ‘" << trait[
"id"] <<
"’ detected in race ‘" << r->id() <<
"’, help page not added again.";
719 for(
const config& trait :
type.possible_traits()) {
720 trait_list.emplace(trait[
"id"], trait);
721 auto it = trait_races.find(trait[
"id"]);
722 const bool is_not_racial_trait = it == trait_races.end() || it->second.find(
type.race_id()) == it->second.end();
726 && is_not_racial_trait)
728 if(!trait_units[trait[
"id"]].
insert(
type.id()).second) {
729 WRN_HP <<
"Duplicate trait ‘" << trait[
"id"] <<
"’ detected in unit type ‘" <<
type.id() <<
"’, help page not added again.";
736 std::vector<topic> topics;
737 for(
auto& [trait_id, trait] : trait_list) {
738 std::string
id =
"traits_" + trait_id;
740 std::string name = trait[
"male_name"].str();
741 if(name.empty()) name = trait[
"female_name"].str();
742 if(name.empty()) name = trait[
"name"].str();
743 if(name.empty())
continue;
745 std::stringstream text;
746 if(!trait[
"help_text"].empty()) {
747 text << trait[
"help_text"];
748 }
else if(!trait[
"description"].empty()) {
749 text << trait[
"description"];
751 text <<
_(
"No description available.");
756 topics.emplace_back(name,
id, text.str());
762 if(!trait_races[trait_id].empty()) {
763 text <<
"\n" <<
markup::tag(
"header",
_(
"Races with this trait")) <<
"\n";
767 for(
const auto& race_id : trait_races[trait_id]) {
769 if (
i < PAGE_LIMIT) {
777 add_remaining_pages(topics, name,
id,
"_races", trait_races[trait_id]);
782 if(!trait_units[trait_id].empty()) {
783 text <<
"\n" <<
markup::tag(
"header",
_(
"Units with this trait")) <<
"\n";
787 for(
const auto& type_id : trait_units[trait_id]) {
789 if (
i < PAGE_LIMIT) {
795 add_remaining_pages(topics, name,
id,
"_units", trait_units[trait_id]);
802 topics.emplace_back(name,
id, text.str());
806 std::sort(topics.begin(), topics.end(),
title_less());
823 }
else if(!
type->hide_help()) {
824 std::string name =
type->type_name();
827 const std::string section_prefix =
type->show_variations_in_help() ?
".." :
"";
840 std::vector<std::string> links_list;
841 for(
const std::string& type_id : type_id_list) {
843 if(!unit_link.empty())
844 links_list.push_back(unit_link);
848 std::sort(links_list.begin(), links_list.end());
855 std::set<std::string, string_less> races;
856 std::set<std::string, string_less> visible_races;
862 races.insert(
type.race_id());
863 if(!
type.hide_help())
864 visible_races.insert(
type.race_id());
869 std::set<std::string, string_less> last_sweep = visible_races;
870 while(!last_sweep.empty()) {
871 std::set<std::string, string_less> current_sweep;
872 for(
const auto& race_id : last_sweep) {
874 const auto& help_taxonomy = r->help_taxonomy();
875 if(!help_taxonomy.empty() && !visible_races.count(help_taxonomy) &&
unit_types.
find_race(help_taxonomy)) {
876 current_sweep.insert(help_taxonomy);
877 races.insert(help_taxonomy);
878 visible_races.insert(help_taxonomy);
882 last_sweep = std::move(current_sweep);
885 struct taxonomy_queue_type
887 std::string parent_id;
890 std::vector<taxonomy_queue_type> taxonomy_queue;
894 for(
const auto& race_id : races) {
897 bool hidden = (visible_races.count(race_id) == 0);
902 std::string help_taxonomy;
904 title = r->plural_name();
905 help_taxonomy = r->help_taxonomy();
907 title =
_(
"race^Miscellaneous");
910 section_cfg[
"title"] = title;
912 section_cfg[
"sections_generator"] =
"units:" + race_id;
913 section_cfg[
"generator"] =
"units:" + race_id;
917 if(help_taxonomy.empty()) {
920 bool parent_hidden = (visible_races.count(help_taxonomy) == 0);
922 taxonomy_queue.push_back({std::move(parent_id), std::move(race_section)});
927 bool process_queue_again =
true;
928 while(process_queue_again && !taxonomy_queue.empty()) {
929 process_queue_again =
false;
930 auto to_process = std::exchange(taxonomy_queue, {});
932 for(
auto& x : to_process) {
935 parent->add_section(x.content);
936 process_queue_again =
true;
938 taxonomy_queue.push_back(std::move(x));
944 for(
auto& x : taxonomy_queue) {
951 std::set<std::string> era_ids;
953 if(era[
"hide_help"].to_bool()) {
958 section_cfg[
"id"] =
era_prefix + era[
"id"].str();
959 section_cfg[
"title"] = era[
"name"];
960 section_cfg[
"generator"] =
"era:" + era[
"id"].str();
962 const bool is_added = era_ids.
insert(section_cfg[
"id"]).second;
964 DBG_HP <<
"Adding help section: " << era[
"id"].str();
966 WRN_HP <<
"Duplicate era with id ‘" << era[
"id"] <<
"’, (help id ‘" << section_cfg[
"id"] <<
"’) detected";
977 WRN_HP <<
"When building terrain help sections, couldn't acquire terrain types data, aborting.";
981 std::map<std::string, section> base_map;
989 bool hidden =
info.hide_help();
992 ==
prefs::get().encountered_terrains().end() && !
info.is_overlay())
998 std::make_shared<terrain_topic_generator>(
info)
1003 if(
info.has_default_base()) {
1004 for(
const auto& base : tdata->get_terrain_info(
info.default_base()).union_type()) {
1006 base_terrains.emplace_back(base);
1013 const terrain_type& base_info = tdata->get_terrain_info(base);
1018 section& base_section = base_map[base_info.
id()];
1023 if(base_info.
id() ==
info.id())
1025 base_section.
topics.push_back(terrain_topic);
1029 std::vector<section> sorted_sections;
1030 for(
const auto& pair : base_map) {
1031 sorted_sections.push_back(pair.second);
1034 std::sort(sorted_sections.begin(), sorted_sections.end(),
section_less());
1036 for(
const section&
s : sorted_sections) {
1043 for(
const unit_type_data::unit_type_map::value_type&
i :
unit_types.
types()) {
1046 if(
type.race_id() != race)
1049 if(!
type.show_variations_in_help())
1053 for(
const std::string& variation_id :
type.variations()) {
1055 const unit_type& var_type =
type.get_variation(variation_id);
1059 base_unit.
topics.emplace_back(topic_name, var_ref, std::make_shared<unit_topic_generator>(var_type, variation_id));
1062 const std::string type_name =
type.type_name();
1065 base_unit.
id = ref_id;
1066 base_unit.
title = type_name;
1074 std::vector<topic> topics;
1075 std::set<std::string, string_less> race_units;
1076 std::set<std::string, string_less> race_topics;
1077 std::set<std::string> alignments;
1081 if(
type.race_id() != race)
1089 const std::string type_name =
type.type_name() + (
type.id() ==
type.type_name().str() ?
"" : debug_suffix);
1090 const std::string real_prefix =
type.show_variations_in_help() ?
".." :
"";
1092 topics.emplace_back(type_name, ref_id, std::make_shared<unit_topic_generator>(
type));
1094 if(!
type.hide_help()) {
1098 race_units.insert(link);
1105 std::string race_id =
"..race_"+race;
1106 std::string race_name;
1107 std::string race_description;
1108 std::string race_help_taxonomy;
1110 race_name = r->plural_name();
1111 race_description = r->description();
1112 race_help_taxonomy = r->help_taxonomy();
1114 for(
const config& additional_topic : r->additional_topics())
1116 std::string
id = additional_topic[
"id"];
1117 std::string title = additional_topic[
"title"];
1118 std::string text = additional_topic[
"text"];
1120 topics.emplace_back(title,
id, text);
1122 race_topics.insert(link);
1125 race_name =
_ (
"race^Miscellaneous");
1130 std::map<std::string, t_string> subgroups;
1132 if(r.second.help_taxonomy() == race) {
1133 if(!r.second.plural_name().empty())
1134 subgroups[r.first] = r.second.plural_name();
1136 subgroups[r.first] = r.first;
1140 std::stringstream text;
1142 if(!race_description.empty()) {
1143 text << race_description <<
"\n\n";
1146 if(!alignments.empty()) {
1148 text << (alignments.size() > 1 ?
_(
"Alignments: ") :
_(
"Alignment: ")) << *(it++);
1149 while(it != alignments.end()) {
1150 text <<
", " << *(it++);
1155 if(!race_help_taxonomy.empty()) {
1157 symbols[
"topic_id"] =
"..race_"+race_help_taxonomy;
1159 symbols[
"help_taxonomy"] = r->plural_name();
1163 symbols[
"help_taxonomy"] = race_help_taxonomy;
1167 text <<
VGETTEXT(
"This is a group of units, all of whom are <ref dst='$topic_id'>$help_taxonomy</ref>.", symbols) <<
"\n\n";
1170 if(!subgroups.empty()) {
1171 if(!race_help_taxonomy.empty()) {
1172 text <<
markup::tag(
"header",
_(
"Subgroups of units within this group")) <<
"\n";
1174 text <<
markup::tag(
"header",
_(
"Groups of units within this race")) <<
"\n";
1176 for(
const auto& sg : subgroups) {
1182 if(!race_help_taxonomy.empty()) {
1183 text <<
markup::tag(
"header",
_(
"Units of this group")) <<
"\n";
1185 text <<
markup::tag(
"header",
_(
"Units of this race")) <<
"\n";
1187 for(
const auto& u : race_units) {
1191 topics.emplace_back(race_name, race_id, text.str());
1194 std::sort(topics.begin(), topics.end(),
title_less());
1202 if(
type.id() ==
"Fog Clearer") {
1212 if(encountered_units.find(
type.id()) != encountered_units.end()) {
1221 auto section_cfg = help_cfg.
find_child(
"section",
"id", section_name);
1223 return std::string();
1226 std::ostringstream res;
1231 typedef std::pair<std::string,std::string> link;
1232 std::vector<link> topics_links;
1235 for(
const std::string&
topic : topics) {
1237 std::string
id = topic_cfg[
"id"];
1239 topics_links.emplace_back(topic_cfg[
"title"],
id);
1243 if(section_cfg[
"sort_topics"] ==
"yes") {
1244 std::sort(topics_links.begin(),topics_links.end());
1247 for(
const auto& [text, target] : topics_links) {
1257 std::stringstream res;
1287 return sec.
id ==
id;
1313 topic_list::const_iterator tit =
1315 if(tit != sec.
topics.end()) {
1334 for(
const auto& subsection : sec.
sections) {
1352 std::vector<std::string> hidden_sections;
1353 std::vector<std::string> hidden_topics;
1358 const std::string
id =
section[
"id"];
1364 hidden_sections.push_back(
id);
1370 const std::string
id =
topic[
"id"];
1372 if(
find_topic(toplevel_section,
id) ==
nullptr) {
1374 hidden_topics.push_back(
id);
1380 if(hidden_sections.empty() && hidden_topics.empty()) {
1381 return {std::move(toplevel_section),
section{}};
1384 config hidden_config = help_config;
1395 return {std::move(toplevel_section),
parse_config(hidden_config)};
1397 }
catch(
const parse_error&
e) {
1398 PLAIN_LOG <<
"Parse error when parsing help text: '" <<
e.message <<
"'";
1404 return (hidden ?
"." :
"");
1408 return (
id.empty() ||
id[0] !=
'.');
1417 if(
id ==
"toplevel") {
1429 if(
id ==
"hidden") {
int generic_combat_modifier(int lawful_bonus, unit_alignments::type 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.
Various functions that implement attacks and attack calculations.
Variant for storing WML attributes.
bool empty() const
Tests for an attribute that either was never set or was set to "".
A config object defines a single node in a WML file, with access to child nodes.
config & add_child(std::string_view key)
void insert(std::string_view key, T &&value)
Inserts an attribute into the config.
optional_config_impl< config > optional_child(std::string_view key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
child_itors child_range(std::string_view key)
void clear_children(T... keys)
optional_config_impl< config > find_child(std::string_view key, const std::string &name, const std::string &value)
Returns the first child of tag key with a name attribute containing value.
std::string debug() const
static game_config_manager * get()
const game_config_view & game_config() const
const config & child_or_empty(std::string_view key) const
optional_const_config find_child(std::string_view key, const std::string &name, const std::string &value) const
To be used as a function object to locate sections and topics with a specified ID.
To be used as a function object when sorting section lists on the title.
To be used as a function object when sorting topic lists on the title.
std::shared_ptr< topic_generator > generator_
const config & parsed_text() const
std::set< std::string > & encountered_units()
std::string base_str() const
static terrain_type_data * get()
bool hide_help() const
For instances created from a [terrain_type] tag, the value in the tag (with default false).
bool is_nonnull() const
True if this object represents some sentinel values.
const std::string & id() const
const t_string & editor_name() const
int get_max_liminal_bonus() const
const std::vector< time_of_day > & times() const
std::string get_help_topic_id() const
const t_string & plural_name() const
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.
const race_map & races() const
const unit_race * find_race(const std::string &) const
const unit_type_map & types() const
config_array_view traits() const
A single unit type that the player may recruit.
const std::string & id() const
The id for this unit_type.
const t_string & variation_name() const
static std::string _(const char *str)
std::string id
Text to match against addon_info.tags()
static lg::log_domain log_help("help")
Standard logging facilities (interface).
const std::string unicode_bullet
Game configuration data as global variables.
std::string hidden_symbol(bool hidden)
@ HIDDEN_BUT_SHOW_MACROS
Although the unit itself is hidden, traits reachable via this unit are not hidden.
@ NO_DESCRIPTION
Ignore this unit for documentation purposes.
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,...
std::vector< topic > generate_time_of_day_topics(const bool)
section parse_config_internal(const config &help_cfg, const config §ion_cfg, int level)
Recursive function used by parse_config.
void generate_terrain_sections(section &sec, int)
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 unit_prefix
const std::string variation_prefix
bool is_visible_id(const std::string &id)
std::vector< topic > generate_faction_topics(const config &era, const bool sort_generated)
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.
const std::string race_prefix
const std::string ability_prefix
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)
std::vector< topic > generate_topics(const bool sort_generated, const std::string &generator)
void generate_sections(const config &help_cfg, const std::string &generator, section &sec, int level)
Dispatch generators to their appropriate functions.
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.
void generate_races_sections(const config &help_cfg, section &sec, int level)
void generate_unit_sections(const config &, section &sec, int, const bool, const std::string &race)
static std::string time_of_day_bonus_colored(const int time_of_day_bonus)
const std::string terrain_prefix
std::string generate_contents_links(const std::string §ion_name, const config &help_cfg)
std::vector< topic > generate_weapon_special_topics(const bool sort_generated)
std::string generate_topic_text(const std::string &generator, const config &help_cfg, const section &sec)
std::vector< topic > generate_ability_topics(const bool sort_generated)
const std::string unknown_unit_topic
const int max_section_level
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 is_valid_id(const std::string &id)
Return true if the id is valid for user defined topics and sections.
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.
std::vector< topic > generate_era_topics(const std::string &era_id, const bool sort_generated)
std::vector< topic > generate_trait_topics(const bool sort_generated)
void generate_era_sections(const config &help_cfg, section &sec, int level)
const std::string default_show_topic
const std::string weaponspecial_prefix
std::pair< section, section > generate_contents()
Generate the help contents from the configurations given to the manager.
section parse_config(const config &cfg)
Parse a help config, return the top level section.
std::vector< topic > generate_unit_topics(const std::string &race, const bool sort_generated)
const std::string era_prefix
const std::string faction_prefix
bool is_scope_active(scope s)
Functions to load and save images from/to disk.
std::string italic(Args &&... data)
Applies italic Pango markup to the input.
std::string img(const std::string &src, const std::string &align, bool floating)
Generates a Help markup tag corresponding to an image.
std::string make_link(const std::string &text, const std::string &dst)
Generates a Help markup tag corresponding to a reference or link.
std::string span_color(const color_t &color, Args &&... data)
Applies Pango markup to the input specifying its display color.
std::string tag(std::string_view tag, Args &&... data)
Wraps the given data in the specified tag.
config parse_text(const std::string &text)
Parse a xml style marked up text string.
rng * generator
This generator is automatically synced during synced context.
::tod_manager * tod_manager
std::vector< terrain_code > ter_list
int compare(const std::string &s1, const std::string &s2)
Case-sensitive lexicographical comparison.
std::string & insert(std::string &str, const std::size_t pos, const std::string &insert)
Insert a UTF-8 string at the specified position.
@ STRIP_SPACES
REMOVE_EMPTY: remove empty elements.
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...
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
auto * find_if(Container &container, const Predicate &predicate)
Convenience wrapper for using find_if on a container without needing to comare to end()
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
std::map< std::string, t_string > string_map
std::vector< std::string > split(const config_attribute_value &val)
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
std::string::const_iterator iterator
Thrown when the help system fails to parse something.
A section contains topics and sections along with title and ID.
bool operator<(const section &) const
Comparison on the ID.
void add_section(const section &s)
Allocate memory for and add the section.
bool operator==(const section &) const
Two sections are equal if their IDs are equal.
A topic contains a title, an id and some text.
bool operator==(const topic &) const
Two topics are equal if their IDs are equal.
bool operator<(const topic &) const
Comparison on the ID.
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Object which defines a time of day with associated bonuses, image, sounds etc.
static map_location::direction s
unit_type_data unit_types