15 #define GETTEXT_DOMAIN "wesnoth-help" 41 #define WRN_HP LOG_STREAM(warn, log_help) 42 #define DBG_HP LOG_STREAM(debug, log_help) 63 std::string lang_policy = (best ?
_(
"Best of") :
_(
"Worst of"));
64 std::string color_policy = (best ?
"green":
"red");
66 return "<format>color='" + color_policy +
"' text='" + lang_policy +
"'</format>";
69 typedef t_translation::ter_list::const_iterator
ter_iter;
71 static std::string
print_behavior_description(ter_iter
start, ter_iter end,
const std::shared_ptr<terrain_type_data> & tdata,
bool first_level =
true,
bool begin_best =
true)
74 if (start == end)
return "";
77 std::optional<ter_iter> last_change_pos;
79 bool best = begin_best;
80 for (ter_iter
i = start;
i != end; ++
i) {
89 if (!last_change_pos) {
90 std::vector<std::string>
names;
91 for (ter_iter
i = start;
i != end; ++
i) {
97 if (names.empty())
return "";
98 if (names.size() == 1)
return names.at(0);
101 if (!first_level) ss <<
"( ";
104 for (std::size_t
i = 1;
i < names.size();
i++) {
105 ss <<
", " << names.at(
i);
108 if (!first_level) ss <<
" )";
110 std::vector<std::string>
names;
111 for (ter_iter
i = *last_change_pos+1;
i != end; ++
i) {
122 if (!first_level) ss <<
"( ";
125 for (
const std::string &
s : names) {
128 if (!first_level) ss <<
" )";
134 std::stringstream ss;
136 if (!type_.icon_image().empty())
137 ss <<
"<img>src='images/buttons/icon-base-32.png~RC(magenta>" << type_.id()
138 <<
")~BLIT("<<
"terrain/" << type_.icon_image() <<
"_30.png)" <<
"'</img> ";
140 if (!type_.editor_image().empty())
141 ss <<
"<img>src='" << type_.editor_image() <<
"'</img> ";
143 if (!type_.help_topic_text().empty())
144 ss <<
"\n\n" << type_.help_topic_text().str() <<
"\n";
151 WRN_HP <<
"When building terrain help topics, we couldn't acquire any terrain types data\n";
155 if (!type_.is_indivisible()) {
156 ss <<
"\n" <<
_(
"Base Terrain: ");
159 for (
const auto& underlying_terrain : type_.union_type()) {
160 const terrain_type& base = tdata->get_terrain_info(underlying_terrain);
176 ss <<
"\n" <<
_(
"Movement properties: ");
180 ss <<
"\n" <<
_(
"Defense properties: ");
187 ss <<
"ID: " << type_.id() <<
"\n";
189 ss <<
"Village: " << (type_.is_village() ?
"Yes" :
"No") <<
"\n";
190 ss <<
"Gives Healing: " << type_.gives_healing() <<
"\n";
192 ss <<
"Keep: " << (type_.is_keep() ?
"Yes" :
"No") <<
"\n";
193 ss <<
"Castle: " << (type_.is_castle() ?
"Yes" :
"No") <<
"\n";
195 ss <<
"Overlay: " << (type_.is_overlay() ?
"Yes" :
"No") <<
"\n";
196 ss <<
"Combined: " << (type_.is_combined() ?
"Yes" :
"No") <<
"\n";
197 ss <<
"Nonnull: " << (type_.is_nonnull() ?
"Yes" :
"No") <<
"\n";
199 ss <<
"Terrain string:" << type_.number() <<
"\n";
201 ss <<
"Hide in Editor: " << (type_.hide_in_editor() ?
"Yes" :
"No") <<
"\n";
202 ss <<
"Editor Group: " << type_.editor_group() <<
"\n";
204 ss <<
"Light Bonus: " << type_.light_bonus(0) <<
"\n";
206 ss << type_.income_description();
208 if (type_.editor_image().empty()) {
209 ss <<
"\nEditor Image: Empty\n";
211 ss <<
"\nEditor Image: " << type_.editor_image() <<
"\n";
215 ss <<
"\nDebug Mvt Description String:";
221 ss <<
"\nDebug Def Description String:";
240 ss <<
make_link(l[i].first, l[i].second);
243 for(i++; i < l.size(); i++) {
244 ss <<
", " <<
make_link(l[i].first,l[i].second);
252 std::stringstream ss;
253 std::string clear_stringstream;
254 const std::string detailed_description = type_.unit_description();
260 ss <<
_(
"Level") <<
" " << type_.level();
263 ss <<
"<img>src='" << male_type.
image();
264 ss <<
"~RC(" << male_type.
flag_rgb() <<
">red)";
265 if (screen_width >= 1200) ss <<
"~XBRZ(2)";
266 ss <<
"' box='no'</img> ";
269 if (&female_type != &male_type) {
270 ss <<
"<img>src='" << female_type.
image();
271 ss <<
"~RC(" << female_type.
flag_rgb() <<
">red)";
272 if (screen_width >= 1200) ss <<
"~XBRZ(2)";
273 ss <<
"' box='no'</img> ";
276 const std::string &male_portrait = male_type.
small_profile().empty() ?
278 const std::string &female_portrait = female_type.
small_profile().empty() ?
281 const bool has_male_portrait = !male_portrait.empty() && male_portrait != male_type.
image() && male_portrait !=
"unit_image";
282 const bool has_female_portrait = !female_portrait.empty() && female_portrait != male_portrait && female_portrait != female_type.
image() && female_portrait !=
"unit_image";
284 int sz = (has_male_portrait && has_female_portrait ? 300 : 400);
285 if (screen_width <= 1366) {
286 sz = (has_male_portrait && has_female_portrait ? 200 : 300);
287 }
else if (screen_width >= 1920) {
292 if (has_male_portrait) {
293 ss <<
"<img>src='" << male_portrait <<
"~FL(horiz)~SCALE_INTO(" << sz <<
',' << sz <<
")' box='no' align='right' float='yes'</img> ";
297 if (has_female_portrait) {
298 ss <<
"<img>src='" << female_portrait <<
"~FL(horiz)~SCALE_INTO(" << sz <<
',' << sz <<
")' box='no' align='right' float='yes'</img> ";
305 const bool first_reverse_value =
true;
306 bool reverse = first_reverse_value;
307 if (variation_.empty()) {
309 std::vector<std::string> adv_units =
310 reverse ? type_.advances_from() : type_.advances_to();
313 for (
const std::string &adv : adv_units) {
321 ss <<
_(
"Advances from: ");
323 ss <<
_(
"Advances to: ");
330 std::string lang_unit = type->
type_name();
346 }
while(reverse != first_reverse_value);
349 const unit_type* parent = variation_.empty() ? &type_ :
351 if (!variation_.empty()) {
355 for (
const std::string& base_id :
utils::split(type_.get_cfg()[
"base_ids"])) {
357 ss <<
_(
"Base units: ");
367 for (
const std::string &var_id : parent->
variations()) {
375 ss <<
_(
"Variations: ");
396 const std::string race_id = type_.race_id();
397 std::string race_name = type_.race()->plural_name();
398 if (race_name.empty()) {
399 race_name =
_ (
"race^Miscellaneous");
402 ss <<
make_link(race_name,
"..race_" + race_id);
408 std::vector<trait_data> must_have_traits;
409 std::vector<trait_data> random_traits;
410 int must_have_nameless_traits = 0;
412 for(
const config& trait : traits) {
413 const std::string& male_name = trait[
"male_name"].str();
414 const std::string& female_name = trait[
"female_name"].str();
415 std::string trait_name;
416 if (type_.has_gender_variation(
unit_race::MALE) && ! male_name.empty())
417 trait_name = male_name;
419 trait_name = female_name;
420 else if (! trait[
"name"].str().empty())
421 trait_name = trait[
"name"].str();
426 if (lang_trait_name.empty() && trait[
"availability"].str() ==
"musthave") {
427 ++must_have_nameless_traits;
430 const std::string ref_id =
"traits_"+trait[
"id"].str();
431 ((trait[
"availability"].str() ==
"musthave") ? must_have_traits : random_traits).emplace_back(lang_trait_name, ref_id);
434 bool line1 = !must_have_traits.empty();
435 bool line2 = !random_traits.empty() && type_.num_traits() > must_have_traits.size();
438 std::string traits_label =
_(
"Traits");
441 std::stringstream must_have_count;
442 must_have_count <<
" (" << must_have_traits.size() <<
") : ";
443 std::stringstream random_count;
444 random_count <<
" (" << (type_.num_traits() - must_have_traits.size() - must_have_nameless_traits) <<
") : ";
450 ss << must_have_count.str();
452 ss <<
"\n" <<
jump(second_line_whitespace) << random_count.str();
461 ss <<
_(
"Traits") <<
" (" << (type_.num_traits() - must_have_nameless_traits) <<
") : ";
470 if(!type_.abilities_metadata().empty()) {
471 ss <<
_(
"Abilities: ");
475 for(
auto iter = type_.abilities_metadata().begin(); iter != type_.abilities_metadata().end(); ++iter) {
476 const std::string ref_id =
ability_prefix + iter->id + iter->name.base_str();
478 if(iter->name.empty()) {
496 if(!type_.adv_abilities_metadata().empty()) {
497 ss <<
_(
"Ability Upgrades: ");
501 for(
auto iter = type_.adv_abilities_metadata().begin(); iter != type_.adv_abilities_metadata().end(); ++iter) {
502 const std::string ref_id =
ability_prefix + iter->id + iter->name.base_str();
504 if(iter->name.empty()) {
532 if (type_.vision() != type_.movement()) {
538 if (type_.jamming() > 0) {
552 <<
make_link(type_.alignment_description(type_.alignment(), type_.genders().front()),
"time_of_day")
554 if (type_.can_advance() || type_.modification_advancements()) {
558 ss <<
_(
"Required\u00a0XP:") <<
font::nbsp << type_.experience_needed();
562 ss <<
"\n\n" << detailed_description;
563 if(type_.has_special_notes()) {
564 ss <<
"\n\n" <<
_(
"Special Notes:") <<
'\n';
565 for(
const auto& note : type_.special_notes()) {
566 ss <<
"• " << note <<
'\n';
571 if (!type_.attacks().empty()) {
573 ss <<
"\n\n<header>text='" <<
escape(
_(
"unit help^Attacks"))
577 std::vector<item> first_row;
579 first_row.push_back(
item(
"", 0));
580 push_header(first_row,
_(
"unit help^Name"));
581 push_header(first_row,
_(
"Strikes"));
582 push_header(first_row,
_(
"Range"));
583 push_header(first_row,
_(
"Type"));
584 push_header(first_row,
_(
"Special"));
585 table.push_back(first_row);
588 std::string lang_weapon = attack.name();
589 std::string lang_type =
string_table[
"type_" + attack.type()];
590 std::vector<item> row;
591 std::stringstream attack_ss;
594 attack_ss <<
"<img>src='" << attack.icon() <<
"'</img>";
595 row.emplace_back(attack_ss.str(),
image_width(attack.icon()));
596 attack_ss.str(clear_stringstream);
603 <<
" " << attack.accuracy_parry_description();
605 attack_ss.str(clear_stringstream);
608 const auto padding = 5;
611 const std::string range_icon =
"icons/profiles/" + attack.range() +
"_attack.png";
612 if (attack.min_range() > 1 || attack.max_range() > 1) {
613 attack_ss << attack.min_range() <<
"-" << attack.max_range() <<
' ';
617 attack_ss.str(clear_stringstream);
620 const std::string type_icon =
"icons/profiles/" + attack.type() +
".png";
625 std::vector<std::pair<t_string, t_string>> specials = attack.special_tooltips();
626 if (!specials.empty()) {
627 std::string lang_special =
"";
628 const std::size_t specials_size = specials.size();
629 for (std::size_t
i = 0;
i != specials_size; ++
i) {
630 const std::string ref_id = std::string(
"weaponspecial_")
631 + specials[
i].first.base_str();
632 lang_special = (specials[
i].first);
633 attack_ss <<
make_link(lang_special, ref_id);
634 if (
i+1 != specials_size) {
640 table.push_back(row);
646 movetype movement_type = type_.movement_type();
648 if (!traits.empty() && type_.num_traits() > 0) {
649 for (
const config &
t : traits) {
650 if (
t[
"availability"].str() ==
"musthave") {
651 for (
const config & effect :
t.child_range(
"effect")) {
652 if (!effect.child(
"filter")
654 movement_type.
merge(effect, effect[
"replace"].to_bool());
662 ss <<
"\n\n<header>text='" <<
escape(
_(
"Resistances"))
665 std::vector<item> first_res_row;
666 push_header(first_res_row,
_(
"Attack Type"));
667 push_header(first_res_row,
_(
"Resistance"));
668 resistance_table.push_back(first_res_row);
670 for(std::pair<std::string, std::string> dam_it : dam_tab) {
671 std::vector<item> row;
672 int resistance = 100;
674 resistance -= std::stoi(dam_it.second);
675 }
catch(std::invalid_argument&) {}
676 std::string resist = std::to_string(resistance) +
'%';
677 const std::size_t pos = resist.find(
'-');
678 if (pos != std::string::npos) {
682 std::string lang_weapon =
string_table[
"type_" + dam_it.first];
684 std::stringstream str;
685 str <<
"<format>color=\"" << color <<
"\" text='"<< resist <<
"'</format>";
686 const std::string markup = str.str();
687 str.str(clear_stringstream);
690 resistance_table.push_back(row);
696 ss <<
"\n\n<header>text='" <<
escape(
_(
"Terrain Modifiers"))
698 std::vector<item> first_row;
700 push_header(first_row,
_(
"Terrain"));
701 push_header(first_row,
_(
"Defense"));
702 push_header(first_row,
_(
"Movement Cost"));
705 if (has_terrain_defense_caps) {
706 push_header(first_row,
_(
"Defense Cap"));
709 const bool has_vision = type_.movement_type().has_vision_data();
711 push_header(first_row,
_(
"Vision Cost"));
713 const bool has_jamming = type_.movement_type().has_jamming_data();
715 push_header(first_row,
_(
"Jamming Cost"));
718 table.push_back(first_row);
720 std::set<terrain_movement_info> terrain_moves;
728 const bool cannot_move = moves > type_.movement();
745 terrain_moves.insert(movement_info);
751 std::vector<item> row;
753 bool high_res =
false;
754 const std::string tc_base = high_res ?
"images/buttons/icon-base-32.png" :
"images/buttons/icon-base-16.png";
755 const std::string terrain_image =
"icons/terrain/terrain_type_" + m.id + (high_res ?
"_30.png" :
".png");
757 const std::string final_image = tc_base +
"~RC(magenta>" + m.id +
")~BLIT(" + terrain_image +
")";
759 row.emplace_back(
"<img>src='" + final_image +
"'</img> " +
767 std::stringstream str;
768 str <<
"<format>color='" << color <<
"' text='"<< m.defense <<
"%'</format>";
769 std::string markup = str.str();
770 str.str(clear_stringstream);
771 str << m.defense <<
"%";
775 str.str(clear_stringstream);
776 bool cannot_move = m.movement_cost > type_.movement();
779 }
else if (m.movement_cost > 1) {
784 str <<
"<format>color=" << color <<
" text='";
787 if(cannot_move && (m.movement_cost > type_.movement() + 5)) {
790 str << m.movement_cost;
794 str.str(clear_stringstream);
795 str << m.movement_cost;
799 if (has_terrain_defense_caps) {
800 str.str(clear_stringstream);
802 str <<
"<format>color='"<< color <<
"' text='" << m.defense <<
"%'</format>";
808 str.str(clear_stringstream);
810 str << m.defense <<
'%';
819 str.str(clear_stringstream);
820 const bool cannot_view = m.vision_cost > type_.vision();
823 }
else if (m.vision_cost > m.movement_cost) {
825 }
else if (m.vision_cost == m.movement_cost) {
830 str <<
"<format>color=" << color <<
" text='";
833 if(cannot_view && (m.vision_cost > type_.vision() + 5)) {
836 str << m.vision_cost;
840 str.str(clear_stringstream);
841 str << m.vision_cost;
847 str.str(clear_stringstream);
848 const bool cannot_jam = m.jamming_cost > type_.jamming();
851 }
else if (m.jamming_cost > m.vision_cost) {
853 }
else if (m.jamming_cost == m.vision_cost) {
858 str <<
"<format>color=" << color <<
" text='";
861 if (cannot_jam && m.jamming_cost > type_.jamming() + 5) {
864 str << m.jamming_cost;
871 table.push_back(row);
876 WRN_HP <<
"When building unit help topics, the display object was null and we couldn't get the terrain info we need.\n";
int defense_modifier(const t_translation::terrain_code &terrain) const
Returns the defensive value of the indicated terrain.
int vision_cost(const t_translation::terrain_code &terrain, bool slowed=false) const
Returns the cost to see through the indicated terrain.
const std::string ability_prefix
bool operator<(const terrain_movement_info &other) const
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)
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
static lg::log_domain log_help("help")
const std::string unknown_unit_topic
const std::string & big_profile() const
virtual std::string operator()() const
bool terrain_matches(const terrain_code &src, const terrain_code &dest)
Tests whether a specific terrain matches an expression, for matching rules see above.
const std::string & flag_rgb() const
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 bool is_indivisible(t_translation::terrain_code id, const t_translation::ter_list &underlying)
Returns true if a terrain has no underlying types other than itself, in respect of either union...
static CVideo & get_singleton()
const int normal_font_size
void merge(const config &new_cfg, bool overwrite=true)
Merges the given config over the existing data, the config should have zero or more children named "m...
The basic "size" of the unit - flying, small land, large land, etc.
bool capped(const t_translation::terrain_code &terrain) const
Returns whether there is a defense cap associated to this terrain.
unsigned image_width(const std::string &filename)
static std::string _(const char *str)
const std::string terrain_prefix
int jamming_cost(const t_translation::terrain_code &terrain, bool slowed=false) const
Returns the cost to "jam" through the indicated terrain.
std::string bold(const std::string &s)
A single unit type that the player may recruit.
bool is_nonnull() const
True if this object represents some sentinel values.
const terrain_code VOID_TERRAIN
VOID_TERRAIN is used for shrouded hexes.
void build_unit_type(const unit_type &ut, unit_type::BUILD_STATUS status) const
Makes sure the provided unit_type is built to the specified level.
static std::string best_str(bool best)
const t_string & editor_name() const
const std::string unicode_minus
std::string generate_table(const table_spec &tab, const unsigned int spacing)
std::vector< std::vector< help::item > > table_spec
std::shared_ptr< terrain_type_data > load_terrain_types_data()
Load the appropriate terrain types data to use.
const terrain_code FOGGED
bool hide_if_impassable() const
const t_string & type_name() const
The name of the unit in the current language setting.
terrain_defense & get_defense()
static const std::set< std::string > effects
The set of applicable effects for movement types.
virtual std::string operator()() const
void push_header(std::vector< help::item > &row, const std::string &name) const
std::set< t_translation::terrain_code > & encountered_terrains()
utils::string_map damage_table() const
Returns a map from attack types to resistances.
t_translation::ter_list::const_iterator ter_iter
const std::string & id() const
The id for this unit_type.
int get_width(bool as_pixels=true) const
Returns the window renderer width in pixels or screen coordinates.
const std::string unicode_figure_dash
boost::iterator_range< const_child_iterator > const_child_itors
static void print_trait_list(std::stringstream &ss, const std::vector< trait_data > &l)
static std::string gettext(const char *str)
const std::string variation_prefix
std::vector< std::string > variations() const
std::string resistance_color(const int resistance)
std::string make_link(const std::string &text, const std::string &dst)
static map_location::DIRECTION s
bool show_variations_in_help() const
Whether the unit type has at least one help-visible variation.
std::vector< std::string > names
std::string to_hex_string() const
Returns the stored color in rrggbb hex format.
unsigned screen_width
The screen resolution and pixel pitch should be available for all widgets since their drawing method ...
const std::string & small_profile() const
std::string escape(const std::string &s)
Prepend all chars with meaning inside attributes with a backslash.
int movement_cost(const t_translation::terrain_code &terrain, bool slowed=false) const
Returns the cost to move through the indicated terrain.
const t_string & name() const
const std::string & image() const
std::string jump(const unsigned amount)
const ter_match ALL_OFF_MAP
const std::string weapon_numbers_sep
const unit_type & get_variation(const std::string &id) const
color_t red_to_green(int val, bool for_text)
Return a color corresponding to the value val red for val=0 to green for val=100, passing by yellow...
std::vector< std::string > split(const config_attribute_value &val)
static std::string print_behavior_description(ter_iter start, ter_iter end, const std::shared_ptr< terrain_type_data > &tdata, bool first_level=true, bool begin_best=true)
int icompare(const std::string &s1, const std::string &s2)
Case-insensitive lexicographical comparison.
symbol_table string_table
const t_string & variation_name() const
Standard logging facilities (interface).
const std::string & id() const
std::vector< terrain_code > ter_list
const unit_type & get_gender_unit_type(std::string gender) const
Returns a gendered variant of this unit_type.
EXIT_STATUS start(const std::string &filename, bool take_screenshot, const std::string &screenshot_filename)
Main interface for launching the editor from the title screen.
static void reverse(lua_State *L, StkId from, StkId to)
A config object defines a single node in a WML file, with access to child nodes.
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. ...
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::pair< std::string, std::string > trait_data
std::pair< std::string, unsigned > item
bool has_terrain_defense_caps(const std::set< t_translation::terrain_code > &ts) const
Returns whether or not there are any terrain caps with respect to a set of terrains.