50 #include <boost/algorithm/string.hpp>
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)
108 if (ch < 0x2e80)
return false;
112 (ch >= 0x4e00 && ch < 0x9fcf) ||
113 (ch >= 0x3400 && ch < 0x4dbf) ||
114 (ch >= 0x20000 && ch < 0x2a6df) ||
115 (ch >= 0xf900 && ch < 0xfaff) ||
116 (ch >= 0x3190 && ch < 0x319f) ||
119 (ch >= 0x2e80 && ch < 0x2eff) ||
120 (ch >= 0x2f00 && ch < 0x2fdf) ||
121 (ch >= 0x31c0 && ch < 0x31ef) ||
124 (ch >= 0x3104 && ch < 0x312e) ||
125 (ch >= 0x31a0 && ch < 0x31bb) ||
128 (ch >= 0xa490 && ch < 0xa4c7) ||
129 (ch >= 0xa000 && ch < 0xa48d) ||
132 (ch >= 0x3040 && ch <= 0x309f) ||
133 (ch >= 0x30a0 && ch <= 0x30ff) ||
134 (ch >= 0x1b000 && ch <= 0x1b001) ||
137 (ch >= 0x31f0 && ch <= 0x31ff) ||
140 (ch >= 0xac00 && ch < 0xd7af) ||
141 (ch >= 0x1100 && ch <= 0x11ff) ||
142 (ch >= 0xa960 && ch <= 0xa97c) ||
143 (ch >= 0xd7b0 && ch <= 0xd7fb) ||
146 (ch >= 0x3000 && ch < 0x303f) ||
149 (ch >= 0xff00 && ch < 0xffef);
156 const std::vector<std::string> toplevel_refs
158 if (std::find(toplevel_refs.begin(), toplevel_refs.end(), section_id)
159 != toplevel_refs.end()) {
166 const std::vector<std::string> sections_refd
168 if (std::find(sections_refd.begin(), sections_refd.end(), section_id)
169 != sections_refd.end()) {
180 const std::vector<std::string> toplevel_refs
182 if (std::find(toplevel_refs.begin(), toplevel_refs.end(), topic_id)
183 != toplevel_refs.end()) {
190 const std::vector<std::string> topics_refd
192 if (std::find(topics_refd.begin(), topics_refd.end(), topic_id)
193 != topics_refd.end()) {
204 PLAIN_LOG <<
"Maximum section depth has been reached. Maybe circular dependency?";
206 else if (section_cfg !=
nullptr) {
208 std::string
id =
level == 0 ?
"toplevel" : (*section_cfg)[
"id"].str();
211 std::stringstream ss;
212 ss <<
"Invalid ID, used for internal purpose: '" <<
id <<
"'";
216 std::string title =
level == 0 ?
"" : (*section_cfg)[
"title"].str();
219 std::vector<std::string>::const_iterator it;
221 for (it = sections.begin(); it != sections.end(); ++it) {
222 if (
auto child_cfg = help_cfg->
find_child(
"section",
"id", *it))
229 std::stringstream ss;
230 ss <<
"Help-section '" << *it <<
"' referenced from '"
231 <<
id <<
"' but could not be found.";
237 if ((*section_cfg)[
"sort_sections"] ==
"yes") {
241 bool sort_topics =
false;
242 bool sort_generated =
true;
244 if ((*section_cfg)[
"sort_topics"] ==
"yes") {
246 sort_generated =
false;
247 }
else if ((*section_cfg)[
"sort_topics"] ==
"no") {
249 sort_generated =
false;
250 }
else if ((*section_cfg)[
"sort_topics"] ==
"generated") {
252 sort_generated =
true;
253 }
else if (!(*section_cfg)[
"sort_topics"].empty()) {
254 std::stringstream ss;
255 ss <<
"Invalid sort option: '" << (*section_cfg)[
"sort_topics"] <<
"'";
259 std::vector<topic> generated_topics =
263 std::vector<topic> topics;
266 for (it = topics_id.begin(); it != topics_id.end(); ++it) {
267 if (
auto topic_cfg = help_cfg->
find_child(
"topic",
"id", *it))
269 std::string text = topic_cfg[
"text"];
271 topic child_topic(topic_cfg[
"title"], topic_cfg[
"id"], text);
273 std::stringstream ss;
274 ss <<
"Invalid ID, used for internal purpose: '" <<
id <<
"'";
277 topics.push_back(child_topic);
280 std::stringstream ss;
281 ss <<
"Help-topic '" << *it <<
"' referenced from '" <<
id
282 <<
"' but could not be found." << std::endl;
288 std::sort(topics.begin(),topics.end(),
title_less());
289 std::sort(generated_topics.begin(),
291 std::merge(generated_topics.begin(),
292 generated_topics.end(),topics.begin(),topics.end()
297 topics.begin(), topics.end());
299 generated_topics.begin(), generated_topics.end());
307 if (cfg !=
nullptr) {
316 std::vector<topic> res;
323 }
else if (
generator ==
"weapon_specials") {
325 }
else if (
generator ==
"time_of_days") {
331 if (parts.size() > 1 && parts[0] ==
"units") {
333 }
else if (parts[0] ==
"era" && parts.size()>1) {
336 WRN_HP <<
"Found a topic generator that I didn't recognize: " <<
generator;
350 DBG_HP <<
"Generating eras...";
354 if (parts.size() > 1 && parts[0] ==
"units") {
357 WRN_HP <<
"Found a section generator that I didn't recognize: " <<
generator;
364 std::string empty_string =
"";
369 if (parts.size() > 1 && parts[0] ==
"contents") {
370 if (parts[1] ==
"generated") {
398 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>";
403 std::vector<topic> topics;
404 std::stringstream toplevel;
407 toplevel <<
_(
"Only available during a scenario.");
408 topics.emplace_back(
_(
"Time of Day Schedule"),
"..schedule", toplevel.str());
415 const std::string
id =
"time_of_day_" + time.id;
416 const std::string
image =
"<img>src='" + time.image +
"'</img>";
417 const std::string image_lawful =
"<img>src='icons/alignments/alignment_lawful_30.png'</img>";
418 const std::string image_neutral =
"<img>src='icons/alignments/alignment_neutral_30.png'</img>";
419 const std::string image_chaotic =
"<img>src='icons/alignments/alignment_chaotic_30.png'</img>";
420 const std::string image_liminal =
"<img>src='icons/alignments/alignment_liminal_30.png'</img>";
421 std::stringstream text;
434 text <<
image <<
'\n' << time.description.str() <<
'\n' <<
439 '\n' <<
make_link(
_(
"Schedule"),
"..schedule");
441 topics.emplace_back(time.name.str(),
id, text.str());
444 topics.emplace_back(
_(
"Time of Day Schedule"),
"..schedule", toplevel.str());
450 std::vector<topic> topics;
452 std::map<t_string, std::string> special_description;
453 std::map<t_string, std::set<std::string, string_less>> special_units;
455 for (
const unit_type_data::unit_type_map::value_type &type_mapping :
unit_types.
types())
465 std::vector<std::pair<t_string, t_string>> specials = atk.special_tooltips();
466 for ( std::size_t
i = 0;
i != specials.size(); ++
i )
468 special_description.emplace(specials[
i].first, specials[
i].second);
470 if (!
type.hide_help()) {
472 std::string type_name =
type.type_name();
474 const std::string section_prefix =
type.show_variations_in_help() ?
".." :
"";
478 std::string link =
make_link(type_name, ref_id);
479 special_units[specials[
i].first].insert(link);
484 for(
config adv :
type.modification_advancements()) {
486 if(effect[
"apply_to"] ==
"new_attack" && effect.has_child(
"specials")) {
487 for(
config::any_child spec : effect.mandatory_child(
"specials").all_children_range()) {
488 if(!spec.cfg[
"name"].empty()) {
489 special_description.emplace(spec.cfg[
"name"].t_str(), spec.cfg[
"description"].t_str());
490 if(!
type.hide_help()) {
492 std::string type_name =
type.type_name();
494 const std::string section_prefix =
type.show_variations_in_help() ?
".." :
"";
498 std::string link =
make_link(type_name, ref_id);
499 special_units[spec.cfg[
"name"]].insert(link);
503 }
else if(effect[
"apply_to"] ==
"attack" && effect.has_child(
"set_specials")) {
504 for(
config::any_child spec : effect.mandatory_child(
"set_specials").all_children_range()) {
505 if(!spec.cfg[
"name"].empty()) {
506 special_description.emplace(spec.cfg[
"name"].t_str(), spec.cfg[
"description"].t_str());
507 if(!
type.hide_help()) {
509 std::string type_name =
type.type_name();
511 const std::string section_prefix =
type.show_variations_in_help() ?
".." :
"";
515 std::string link =
make_link(type_name, ref_id);
516 special_units[spec.cfg[
"name"]].insert(link);
526 s != special_description.end(); ++
s) {
528 std::string
id =
"weaponspecial_" +
s->first.base_str();
529 std::stringstream text;
531 text <<
"\n\n" <<
_(
"<header>text='Units with this special attack'</header>") <<
"\n";
532 std::set<std::string, string_less> &units = special_units[
s->first];
537 topics.emplace_back(
s->first,
id, text.str());
541 std::sort(topics.begin(), topics.end(),
title_less());
547 std::vector<topic> topics;
549 std::map<std::string, const unit_type::ability_metadata*> ability_topic_data;
550 std::map<std::string, std::set<std::string, string_less>> ability_units;
557 const std::string topic_ref = ability.id + ability.name.base_str();
559 ability_topic_data.emplace(topic_ref, &ability);
561 if(!
type.hide_help()) {
566 ability_units[topic_ref].insert(link);
581 parse(
type, ability);
585 parse(
type, ability);
589 for(
const auto& a : ability_topic_data) {
590 if (a.second->name.empty()) {
593 std::ostringstream text;
594 text << a.second->description;
595 text <<
"\n\n" <<
_(
"<header>text='Units with this ability'</header>") <<
"\n";
597 for(
const auto& u : ability_units[a.first]) {
601 topics.emplace_back(a.second->name,
ability_prefix + a.first, text.str());
605 std::sort(topics.begin(), topics.end(),
title_less());
613 std::vector<topic> topics;
616 if(era && !era[
"hide_help"].to_bool()) {
619 std::vector<std::string> faction_links;
620 for (
const topic &
t : topics) {
624 std::stringstream text;
625 text <<
"<header>text='" <<
_(
"Era:") <<
" " << era[
"name"] <<
"'</header>" <<
"\n";
628 if (!description.
empty()) {
629 text << description.
t_str() <<
"\n";
633 text <<
"<header>text='" <<
_(
"Factions") <<
"'</header>" <<
"\n";
635 std::sort(faction_links.begin(), faction_links.end());
636 for (
const std::string &link : faction_links) {
640 topic era_topic(era[
"name"],
".." +
era_prefix + era[
"id"].str(), text.str());
642 topics.push_back( era_topic );
649 std::vector<topic> topics;
651 const std::string&
id =
f[
"id"];
655 std::stringstream text;
658 if (!description.
empty()) {
659 text << description.
t_str() <<
"\n";
663 const std::vector<std::string> recruit_ids =
utils::split(
f[
"recruit"]);
664 std::set<std::string> races;
665 std::set<std::string> alignments;
667 for (
const std::string & u_id : recruit_ids) {
675 alignments.insert(
make_link(
type.alignment_description(
type.alignment(),
type.genders().front()),
"time_of_day"));
679 if (!races.empty()) {
681 text <<
_(
"Races: ") << *(it++);
682 while(it != races.end()) {
683 text <<
", " << *(it++);
688 if (!alignments.empty()) {
690 text <<
_(
"Alignments: ") << *(it++);
691 while(it != alignments.end()) {
692 text <<
", " << *(it++);
697 text <<
"<header>text='" <<
_(
"Leaders") <<
"'</header>" <<
"\n";
698 const std::vector<std::string> leaders =
700 for (
const std::string &link : leaders) {
706 text <<
"<header>text='" <<
_(
"Recruits") <<
"'</header>" <<
"\n";
707 const std::vector<std::string> recruit_links =
709 for (
const std::string &link : recruit_links) {
713 const std::string name =
f[
"name"];
715 topics.emplace_back(name, ref_id, text.str());
718 std::sort(topics.begin(), topics.end(),
title_less());
727 std::map<t_string, const config> trait_list;
731 trait_list.emplace(trait[
"id"], trait);
735 std::set<std::string> races;
746 races.insert(
type.race_id());
756 for (
const config& trait :
type.possible_traits()) {
757 trait_list.emplace(trait[
"id"], trait);
767 for(
const auto& race_id : races) {
769 for(
const config & trait : r->additional_traits()) {
770 trait_list.emplace(trait[
"id"], trait);
775 std::vector<topic> topics;
776 for(
auto& a : trait_list) {
777 std::string
id =
"traits_" + a.first;
778 const config& trait = a.second;
780 std::string name = trait[
"male_name"].str();
781 if (name.empty()) name = trait[
"female_name"].str();
782 if (name.empty()) name = trait[
"name"].str();
783 if (name.empty())
continue;
785 std::stringstream text;
786 if (!trait[
"help_text"].empty()) {
787 text << trait[
"help_text"];
788 }
else if (!trait[
"description"].empty()) {
789 text << trait[
"description"];
791 text <<
_(
"No description available.");
795 topics.emplace_back(name,
id, text.str());
799 std::sort(topics.begin(), topics.end(),
title_less());
810 PLAIN_LOG <<
"Unknown unit type : " << type_id;
814 }
else if (!
type->hide_help()) {
815 std::string name =
type->type_name();
818 const std::string section_prefix =
type->show_variations_in_help() ?
".." :
"";
832 std::vector<std::string> links_list;
833 for (
const std::string &type_id : type_id_list) {
835 if (!unit_link.empty())
836 links_list.push_back(unit_link);
840 std::sort(links_list.begin(), links_list.end());
847 std::set<std::string, string_less> races;
848 std::set<std::string, string_less> visible_races;
855 races.insert(
type.race_id());
856 if(!
type.hide_help())
857 visible_races.insert(
type.race_id());
862 std::set<std::string, string_less> last_sweep = visible_races;
863 while(!last_sweep.empty()) {
864 std::set<std::string, string_less> current_sweep;
865 for(
const auto& race_id : last_sweep) {
867 const auto& help_taxonomy = r->help_taxonomy();
868 if(!help_taxonomy.empty() && !visible_races.count(help_taxonomy) &&
unit_types.
find_race(help_taxonomy)) {
869 current_sweep.insert(help_taxonomy);
870 races.insert(help_taxonomy);
871 visible_races.insert(help_taxonomy);
875 last_sweep = std::move(current_sweep);
878 struct taxonomy_queue_type
880 std::string parent_id;
883 std::vector<taxonomy_queue_type> taxonomy_queue;
887 for(
const auto& race_id : races) {
891 bool hidden = (visible_races.count(race_id) == 0);
896 std::string help_taxonomy;
898 title = r->plural_name();
899 help_taxonomy = r->help_taxonomy();
901 title =
_(
"race^Miscellaneous");
904 section_cfg[
"title"] = title;
906 section_cfg[
"sections_generator"] =
"units:" + race_id;
907 section_cfg[
"generator"] =
"units:" + race_id;
911 if(help_taxonomy.empty()) {
914 bool parent_hidden = (visible_races.count(help_taxonomy) == 0);
916 taxonomy_queue.push_back({std::move(parent_id), std::move(race_section)});
921 bool process_queue_again =
true;
922 while(process_queue_again && !taxonomy_queue.empty()) {
923 process_queue_again =
false;
924 std::vector<taxonomy_queue_type> to_process = std::move(taxonomy_queue);
926 for(
auto& x : to_process) {
929 parent->add_section(std::move(x.content));
930 process_queue_again =
true;
932 taxonomy_queue.push_back(std::move(x));
938 for(
auto& x : taxonomy_queue) {
946 if (era[
"hide_help"].to_bool()) {
950 DBG_HP <<
"Adding help section: " << era[
"id"].str();
954 section_cfg[
"id"] =
era_prefix + era[
"id"].str();
955 section_cfg[
"title"] = era[
"name"];
957 section_cfg[
"generator"] =
"era:" + era[
"id"].str();
971 WRN_HP <<
"When building terrain help sections, couldn't acquire terrain types data, aborting.";
975 std::map<std::string, section> base_map;
983 bool hidden =
info.hide_help();
986 ==
prefs::get().encountered_terrains().end() && !
info.is_overlay())
990 terrain_topic.
title =
info.editor_name();
992 terrain_topic.
text = std::make_shared<terrain_topic_generator>(
info);
995 if (
info.has_default_base()) {
996 for (
const auto base : tdata->underlying_union_terrain(
info.default_base())) {
998 base_terrains.emplace_back(base);
1004 const terrain_type& base_info = tdata->get_terrain_info(base);
1009 section& base_section = base_map[base_info.
id()];
1014 if (base_info.
id() ==
info.id())
1016 base_section.
topics.push_back(terrain_topic);
1020 for (
const auto& base : base_map) {
1027 for (
const unit_type_data::unit_type_map::value_type &
i :
unit_types.
types()) {
1030 if (
type.race_id() != race)
1033 if (!
type.show_variations_in_help())
1037 for (
const std::string &variation_id :
type.variations()) {
1039 const unit_type &var_type =
type.get_variation(variation_id);
1043 topic var_topic(topic_name, var_ref,
"");
1044 var_topic.
text = std::make_shared<unit_topic_generator>(var_type, variation_id);
1045 base_unit.
topics.push_back(var_topic);
1048 const std::string type_name =
type.type_name();
1051 base_unit.
id = ref_id;
1052 base_unit.
title = type_name;
1060 std::vector<topic> topics;
1061 std::set<std::string, string_less> race_units;
1062 std::set<std::string, string_less> race_topics;
1063 std::set<std::string> alignments;
1065 for (
const unit_type_data::unit_type_map::value_type &
i :
unit_types.
types())
1069 if (
type.race_id() != race)
1077 const std::string type_name =
type.type_name() + (
type.id() ==
type.type_name().str() ?
"" : debug_suffix);
1078 const std::string real_prefix =
type.show_variations_in_help() ?
".." :
"";
1080 topic unit_topic(type_name, ref_id,
"");
1081 unit_topic.
text = std::make_shared<unit_topic_generator>(
type);
1082 topics.push_back(unit_topic);
1084 if (!
type.hide_help()) {
1087 std::string link =
make_link(type_name, ref_id);
1088 race_units.insert(link);
1090 alignments.insert(
make_link(
type.alignment_description(
type.alignment(),
type.genders().front()),
"time_of_day"));
1095 std::string race_id =
"..race_"+race;
1096 std::string race_name;
1097 std::string race_description;
1098 std::string race_help_taxonomy;
1100 race_name = r->plural_name();
1101 race_description = r->description();
1102 race_help_taxonomy = r->help_taxonomy();
1104 for (
const config &additional_topic : r->additional_topics())
1106 std::string
id = additional_topic[
"id"];
1107 std::string title = additional_topic[
"title"];
1108 std::string text = additional_topic[
"text"];
1110 topics.emplace_back(title,
id,text);
1111 std::string link =
make_link(title,
id);
1112 race_topics.insert(link);
1115 race_name =
_ (
"race^Miscellaneous");
1120 std::map<std::string, t_string> subgroups;
1122 if (r.second.help_taxonomy() == race) {
1123 if (!r.second.plural_name().empty())
1124 subgroups[r.first] = r.second.plural_name();
1126 subgroups[r.first] = r.first;
1130 std::stringstream text;
1132 if (!race_description.empty()) {
1133 text << race_description <<
"\n\n";
1136 if (!alignments.empty()) {
1138 text << (alignments.size() > 1 ?
_(
"Alignments: ") :
_(
"Alignment: ")) << *(it++);
1139 while(it != alignments.end()) {
1140 text <<
", " << *(it++);
1145 if (!race_help_taxonomy.empty()) {
1147 symbols[
"topic_id"] =
"..race_"+race_help_taxonomy;
1149 symbols[
"help_taxonomy"] = r->plural_name();
1153 symbols[
"help_taxonomy"] = race_help_taxonomy;
1157 text <<
VGETTEXT(
"This is a group of units, all of whom are <ref>dst='$topic_id' text='$help_taxonomy'</ref>.", symbols) <<
"\n\n";
1160 if (!subgroups.empty()) {
1161 if (!race_help_taxonomy.empty()) {
1162 text <<
_(
"<header>text='Subgroups of units within this group'</header>") <<
"\n";
1164 text <<
_(
"<header>text='Groups of units within this race'</header>") <<
"\n";
1166 for (
const auto &sg : subgroups) {
1172 if (!race_help_taxonomy.empty()) {
1173 text <<
_(
"<header>text='Units of this group'</header>") <<
"\n";
1175 text <<
_(
"<header>text='Units of this race'</header>") <<
"\n";
1177 for (
const auto &u : race_units) {
1181 topics.emplace_back(race_name, race_id, text.str());
1184 std::sort(topics.begin(), topics.end(),
title_less());
1197 if (encountered_units.find(
type.id()) != encountered_units.end()) {
1202 if (
type.id() ==
"Fog Clearer") {
1211 auto section_cfg = help_cfg->
find_child(
"section",
"id", section_name);
1213 return std::string();
1216 std::ostringstream res;
1221 typedef std::pair<std::string,std::string> link;
1222 std::vector<link> topics_links;
1226 for (
t = topics.begin();
t != topics.end(); ++
t) {
1227 if (
auto topic_cfg = help_cfg->
find_child(
"topic",
"id", *
t)) {
1228 std::string
id = topic_cfg[
"id"];
1230 topics_links.emplace_back(topic_cfg[
"title"],
id);
1234 if (section_cfg[
"sort_topics"] ==
"yes") {
1235 std::sort(topics_links.begin(),topics_links.end());
1239 for (l = topics_links.begin(); l != topics_links.end(); ++l) {
1240 std::string link =
make_link(l->first, l->second);
1249 std::stringstream res;
1258 for (
auto &
t : topics) {
1279 return sec.
id ==
id;
1300 topic_list::const_iterator tit =
1302 if (tit != sec.
topics.end()) {
1321 for (
const auto &subsection : sec.
sections) {
1337 std::vector<std::string> res;
1338 bool last_char_escape =
false;
1339 bool found_slash =
false;
1340 bool in_quotes =
false;
1341 const char escape_char =
'\\';
1342 std::stringstream ss;
1345 for (pos = 0; pos < text.size(); ++pos) {
1346 const char c = text[pos];
1347 if (
c == escape_char && !last_char_escape) {
1348 last_char_escape =
true;
1350 if (state ==
OTHER) {
1352 if (last_char_escape) {
1355 res.push_back(ss.str());
1357 state = ELEMENT_NAME;
1362 }
else if (state == ELEMENT_NAME) {
1363 if ((
c ==
'/') && (!in_quotes)) {
1365 }
else if (
c ==
'\'') {
1367 in_quotes = !in_quotes;
1368 }
else if (
c ==
'>') {
1372 std::stringstream
s;
1373 std::string element_name = ss.str();
1377 std::size_t attr_pos = element_name.find(
" ");
1378 std::string attrs =
"";
1379 if (attr_pos != std::string::npos) {
1380 attrs = element_name.substr(attr_pos+1);
1381 element_name = element_name.substr(0, attr_pos);
1387 found_slash =
false;
1388 pos = text.find(
">", pos);
1391 s <<
"</" << element_name <<
">";
1392 const std::string end_element_name =
s.str();
1393 std::size_t end_pos = text.find(end_element_name, pos);
1394 if (end_pos == std::string::npos) {
1395 std::stringstream
msg;
1396 msg <<
"Unterminated element: " << element_name;
1400 const std::string contents = attrs +
" " + text.substr(pos + 1, end_pos - pos - 1);
1401 const std::string element =
convert_to_wml(element_name, contents);
1402 res.push_back(element);
1403 pos = end_pos + end_element_name.size() - 1;
1408 found_slash =
false;
1409 std::string
msg =
"Erroneous / in element name.";
1416 last_char_escape =
false;
1419 if (state == ELEMENT_NAME) {
1420 std::stringstream
msg;
1421 msg <<
"Element '" << ss.str() <<
"' continues through end of string.";
1424 if (!ss.str().empty()) {
1426 res.push_back(ss.str());
1433 std::stringstream ss;
1434 bool in_quotes =
false;
1435 bool last_char_escape =
false;
1436 const char escape_char =
'\\';
1437 std::vector<std::string> attributes;
1438 std::stringstream buff;
1448 for (std::size_t pos = 0; pos < contents.size(); ++pos) {
1449 const char c = contents[pos];
1450 if (
c == escape_char && !last_char_escape) {
1451 last_char_escape =
true;
1454 if (
c ==
'\'' && !last_char_escape) {
1456 in_quotes = !in_quotes;
1458 else if ((
c ==
' ' ||
c ==
'\n') && !last_char_escape && !in_quotes) {
1460 std::size_t eq_pos = ss.str().find(
"=");
1461 if (eq_pos == std::string::npos) {
1463 buff <<
" " << ss.str();
1465 attributes.push_back(ss.str());
1472 last_char_escape =
false;
1477 std::stringstream
msg;
1478 msg <<
"Unterminated single quote after: '" << ss.str() <<
"'";
1482 if (!ss.str().empty()) {
1483 std::size_t eq_pos = ss.str().find(
"=");
1484 if (eq_pos == std::string::npos) {
1486 buff <<
" " << ss.str();
1488 attributes.push_back(ss.str());
1493 ss <<
"[" << element_name <<
"]\n";
1496 for (
auto& elem : attributes) {
1501 std::string text = buff.str();
1503 if (!text.empty()) {
1504 ss <<
"text=\"" << text <<
"\"\n";
1506 ss <<
"[/" << element_name <<
"]";
1513 const unsigned width)
1515 std::vector<std::string> res;
1518 res.push_back(first_line);
1519 if(
s.size() > first_line.size()) {
1520 res.push_back(
s.substr(first_line.size()));
1533 if (text.length() > 0 && text[0] ==
' ') {
1534 return text.substr(1);
1541 std::size_t first_word_start =
s.find_first_not_of(
' ');
1542 if (first_word_start == std::string::npos) {
1545 std::size_t first_word_end =
s.find_first_of(
" \n", first_word_start);
1546 if( first_word_end == first_word_start ) {
1548 first_word_end = first_word_start+1;
1552 std::string re =
s.substr(0, first_word_end);
1558 char32_t firstchar = *ch;
1560 re = unicode_cast<std::string>(firstchar);
1580 std::stringstream ss;
1583 const std::string
id =
section[
"id"];
1589 if (!ss.str().empty()) {
1596 hidden_toplevel[
"sections"] = ss.str();
1600 const std::string
id =
topic[
"id"];
1603 if (!ss.str().empty()) {
1610 hidden_toplevel[
"topics"] = ss.str();
1611 config hidden_cfg = *help_config;
1614 hidden_cfg.
add_child(
"toplevel", std::move(hidden_toplevel));
1618 std::stringstream
msg;
1619 msg <<
"Parse error when parsing help text: '" <<
e.message <<
"'";
1627 return (hidden ?
"." :
"");
1631 return (
id.empty() ||
id[0] !=
'.');
1640 if (
id ==
"toplevel") {
1646 if (
id.
compare(0, 8,
"ability_") == 0) {
1649 if (
id.
compare(0, 14,
"weaponspecial_") == 0) {
1652 if (
id ==
"hidden") {
1664 if (
surf !=
nullptr) {
1670 void push_tab_pair(std::vector<help::item> &v,
const std::string &
s,
const utils::optional<std::string> &
image,
unsigned padding)
1676 padding = (width ? padding : 0);
1678 item.first =
"<img>src='" + *
image +
"'</img>" + (padding ?
jump(padding) :
"") +
s;
1679 item.second += width + padding;
1681 v.emplace_back(
item);
1686 table_spec::const_iterator row_it;
1687 std::vector<std::pair<std::string, unsigned>>::const_iterator col_it;
1688 unsigned int num_cols = 0;
1689 for (row_it = tab.begin(); row_it != tab.end(); ++row_it) {
1690 if (row_it->size() > num_cols) {
1691 num_cols = row_it->size();
1694 std::vector<unsigned int> col_widths(num_cols, 0);
1696 for (row_it = tab.begin(); row_it != tab.end(); ++row_it) {
1697 unsigned int col = 0;
1698 for (col_it = row_it->begin(); col_it != row_it->end(); ++col_it) {
1699 if (col_widths[col] < col_it->second + spacing) {
1700 col_widths[col] = col_it->second + spacing;
1705 std::vector<unsigned int> col_starts(num_cols);
1707 for (
unsigned int i = 0;
i < num_cols; ++
i) {
1708 unsigned int this_col_start = 0;
1709 for (
unsigned int j = 0; j <
i; ++j) {
1710 this_col_start += col_widths[j];
1712 col_starts[
i] = this_col_start;
1714 std::stringstream ss;
1715 for (row_it = tab.begin(); row_it != tab.end(); ++row_it) {
1716 unsigned int col = 0;
1717 for (col_it = row_it->begin(); col_it != row_it->end(); ++col_it) {
1718 ss <<
jump_to(col_starts[col]) << col_it->first;
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.
optional_config_impl< 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.
void clear_children(T... keys)
child_itors child_range(config_key_type key)
std::string debug() const
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
config & add_child(config_key_type key)
virtual const gamemap & map() const =0
const display_context & get_disp_context() const
static display * get_singleton()
Returns the display object if a display object exists.
static game_config_manager * get()
const std::shared_ptr< terrain_type_data > & terrain_types() const
A class grating read only view to a vector of config objects, viewed as one config with all children ...
const config & child_or_empty(config_key_type key) const
optional_const_config find_child(config_key_type key, const std::string &name, const std::string &value) const
config_array_view child_range(config_key_type key) const
const std::shared_ptr< terrain_type_data > & tdata() 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.
The text displayed in a topic.
const std::vector< std::string > & parsed_text() const
std::shared_ptr< topic_generator > generator_
topic_text & operator=(topic_text &&t)=default
std::vector< std::string > parsed_text_
Generic locator abstracting the location of an image.
std::set< std::string > & encountered_units()
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 map_location &loc=map_location::null_location()) const
static iterator_base end(const string_type &str)
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
Thrown by operations encountering invalid UTF-8 data.
map_display and display: classes which take care of displaying the map and game-data on the screen.
static std::string _(const char *str)
std::string id
Text to match against addon_info.tags()
static lg::log_domain log_display("display")
static lg::log_domain log_help("help")
Standard logging facilities (interface).
int pango_line_width(const std::string &line, int font_size, font::pango_text::FONT_STYLE font_style=font::pango_text::STYLE_NORMAL)
Determine the width of a line of text given a certain font size.
const std::string unicode_bullet
std::string pango_word_wrap(const std::string &unwrapped_text, int font_size, int max_width, int max_height, int max_lines, bool)
Uses Pango to word wrap text.
std::string get_first_word(const std::string &s)
Return the first word in s, not removing any spaces in the start of it.
std::vector< topic > generate_unit_topics(const bool sort_generated, const std::string &race)
std::string hidden_symbol(bool hidden)
void generate_unit_sections(const config *, section &sec, int, const bool, const std::string &race)
@ 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.
std::string make_link(const std::string &text, const std::string &dst)
void generate_races_sections(const config *help_cfg, section &sec, int level)
std::string escape(const std::string &s)
Prepend all chars with meaning inside attributes with a backslash.
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)
int last_num_encountered_units
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 open_section_img
const std::string unit_prefix
unsigned image_width(const std::string &filename)
const std::string variation_prefix
void parse_config_internal(const config *help_cfg, const config *section_cfg, section &sec, int level)
Recursive function used by parse_config.
bool is_visible_id(const std::string &id)
const std::string closed_section_img
std::vector< topic > generate_faction_topics(const config &era, const bool sort_generated)
void generate_sections(const config *help_cfg, const std::string &generator, section &sec, int level)
Dispatch generators to their appropriate functions.
const std::string topic_img
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::vector< help::item > > table_spec
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)
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.
std::pair< std::string, unsigned > item
void push_tab_pair(std::vector< help::item > &v, const std::string &s, const utils::optional< std::string > &image, unsigned padding)
static std::string time_of_day_bonus_colored(const int time_of_day_bonus)
const int normal_font_size
void generate_contents()
Generate the help contents from the configurations given to the manager.
std::string generate_table(const table_spec &tab, const unsigned int spacing)
const std::string terrain_prefix
std::vector< topic > generate_weapon_special_topics(const bool sort_generated)
std::vector< topic > generate_ability_topics(const bool sort_generated)
std::string remove_first_space(const std::string &text)
std::string jump_to(const unsigned pos)
const std::string unknown_unit_topic
const int max_section_level
const unsigned max_history
std::vector< std::string > empty_string_vector
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,...
std::shared_ptr< terrain_type_data > load_terrain_types_data()
Load the appropriate terrain types data to use.
std::string generate_topic_text(const std::string &generator, const config *help_cfg, const section &sec, const std::vector< topic > &generated_topics)
std::string convert_to_wml(std::string &element_name, const std::string &contents)
Convert the the text between start and end xml tags for element_name to valid wml attributes,...
bool is_valid_id(const std::string &id)
Return true if the id is valid for user defined topics and sections.
boost::tribool last_debug_state
std::string generate_contents_links(const std::string §ion_name, config const *help_cfg)
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.
help::section default_toplevel
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::string jump(const unsigned amount)
section parse_config(const config *cfg)
Parse a help config, return the top level section.
std::vector< topic > generate_era_topics(const bool sort_generated, const std::string &era_id)
const game_config_view * game_cfg
int last_num_encountered_terrains
help::section hidden_sections
std::vector< topic > generate_trait_topics(const bool sort_generated)
std::vector< std::string > parse_text(const std::string &text)
Parse a xml style marked up text string.
const std::string default_show_topic
void generate_era_sections(const config *help_cfg, section &sec, int level)
static bool is_cjk_char(const char32_t ch)
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.
surface get_surface(const image::locator &i_locator, TYPE type, bool skip_cache)
Returns an image surface suitable for software manipulation.
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.
@ STRIP_SPACES
REMOVE_EMPTY: remove empty elements.
void trim(std::string_view &s)
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.
std::string escape(const std::string &str, const char *special_chars)
Prepends a configurable set of characters with a backslash.
std::map< std::string, t_string > string_map
std::vector< std::string > split(const config_attribute_value &val)
std::string::const_iterator iterator
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Transitional API for porting SDL_ttf-based code to Pango.
std::string filename
Filename.
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