16 #define GETTEXT_DOMAIN "wesnoth-help"
41 #define WRN_HP LOG_STREAM(warn, log_help)
42 #define DBG_HP LOG_STREAM(debug, log_help)
64 std::string best_str(
bool best) {
65 std::string lang_policy = (best ?
_(
"Best of") :
_(
"Worst of"));
66 std::string color_policy = (best ?
"green":
"red");
71 std::string format_mp_entry(
const int cost,
const int max_cost) {
72 std::stringstream str_unformatted;
73 const bool cannot = cost < max_cost;
80 if(cannot && max_cost > cost + 5) {
83 str_unformatted <<
"(" << max_cost <<
")";
85 str_unformatted << max_cost;
88 const int hexes_per_turn = cost / max_cost;
89 str_unformatted <<
" ";
90 for(
int i = 0;
i < hexes_per_turn; ++
i) {
92 str_unformatted <<
"\u2b23\u200b";
99 typedef t_translation::ter_list::const_iterator ter_iter;
101 std::string print_behavior_description(
102 const ter_iter&
start,
104 const std::shared_ptr<terrain_type_data>& tdata,
105 bool first_level =
true,
106 bool begin_best =
true)
109 if(
start == end)
return "";
115 utils::optional<ter_iter> last_change_pos;
117 bool best = begin_best;
125 std::stringstream ss;
127 if(!last_change_pos) {
128 std::vector<std::string>
names;
132 names.push_back(
_(
"base terrain"));
140 if(
names.empty())
return "";
143 ss << best_str(best) <<
" ";
144 if(!first_level) ss <<
"( ";
146 if(!first_level) ss <<
" )";
148 std::vector<std::string>
names;
149 for(ter_iter
i = *last_change_pos+1;
i !=
end; ++
i) {
156 return print_behavior_description(
start, *last_change_pos, tdata, first_level, begin_best);
159 ss << best_str(best) <<
" ";
160 if(!first_level) ss <<
"( ";
161 ss << print_behavior_description(
start, *last_change_pos-1, tdata,
false, begin_best);
163 for(
const std::string&
s :
names) {
166 if(!first_level) ss <<
" )";
174 std::vector<std::string> special_notes;
176 if(
type.is_village()) {
177 special_notes.push_back(
_(
"Villages allow any unit stationed therein to heal, or to be cured of poison."));
178 }
else if(
type.gives_healing() > 0) {
182 auto message =
VNGETTEXT(
"This terrain allows units to be cured of poison, or to heal a single hitpoint.",
183 "This terrain allows units to heal $amount hitpoints, or to be cured of poison, as if stationed in a village.",
184 type.gives_healing(), symbols);
185 special_notes.push_back(std::move(message));
188 if(
type.is_castle()) {
189 special_notes.push_back(
_(
"This terrain is a castle — units can be recruited onto it from a connected keep."));
191 if(
type.is_keep() &&
type.is_castle()) {
193 special_notes.push_back(
_(
"This terrain is a keep — a leader can recruit from this hex onto connected castle hexes."));
194 }
else if(
type.is_keep() && !
type.is_castle()) {
196 special_notes.push_back(
_(
"This unusual keep allows a leader to recruit while standing on it, but does not allow a leader on a connected keep to recruit onto this hex."));
199 return special_notes;
205 std::stringstream ss;
209 <<
"images/buttons/icon-base-32.png~RC(magenta>" <<
type_.
id()
224 WRN_HP <<
"When building terrain help topics, we couldn't acquire any terrain types data";
229 ss <<
"Base terrain: ";
230 const auto base_t = tdata->get_terrain_info(
235 ss <<
"Overlay terrain: ";
236 const auto overlay_t = tdata->get_terrain_info(
239 (overlay_t.hide_help() ?
"." :
"") +
terrain_prefix + overlay_t.id());
243 const auto& notes = get_special_notes(
type_);
245 ss <<
"\n\n" <<
markup::tag(
"header",
_(
"Special Notes")) <<
"\n\n";
246 for(
const auto& note : notes) {
254 std::vector<t_string> underlying;
256 const terrain_type& base = tdata->get_terrain_info(underlying_terrain);
266 ss <<
"\n" <<
VNGETTEXT(
"Basic terrain type: $types",
"Basic terrain types: $types", underlying.size(), symbols);
275 ss <<
"\n" <<
VGETTEXT(
"Typical base terrain: $type", symbols);
281 ss <<
"\n" <<
_(
"Movement properties: ");
282 ss << print_behavior_description(underlying_mvt_terrains.begin(), underlying_mvt_terrains.end(), tdata) <<
"\n";
285 ss <<
"\n" <<
_(
"Defense properties: ");
286 ss << print_behavior_description(underlying_def_terrains.begin(), underlying_def_terrains.end(), tdata) <<
"\n";
292 ss <<
"ID: " <<
type_.
id() <<
"\n";
297 ss <<
"Keep: " << (
type_.
is_keep() ?
"Yes" :
"No") <<
"\n";
305 ss <<
"Terrain string: " <<
type_.
number() <<
"\n";
314 ss <<
"\nEditor Image: ";
320 ss <<
"\nDebug Mvt Description String:";
326 ss <<
"\nDebug Def Description String:";
347 for(
i++;
i < l.size();
i++) {
356 std::stringstream ss;
366 const std::string &male_portrait = male_type.
small_profile().empty() ?
368 const std::string &female_portrait = female_type.
small_profile().empty() ?
371 const bool has_male_portrait = !male_portrait.empty() && male_portrait != male_type.
image() && male_portrait !=
"unit_image";
372 const bool has_female_portrait = !female_portrait.empty() && female_portrait != male_portrait && female_portrait != female_type.
image() && female_portrait !=
"unit_image";
375 if(has_male_portrait) {
376 ss <<
markup::img(male_portrait +
"~FL(horiz)",
"right",
true);
379 if(has_female_portrait) {
380 ss <<
markup::img(female_portrait +
"~FL(horiz)",
"right",
true);
384 if(!male_type.
image().empty()) {
386 << male_type.
image() <<
"~RC(" << male_type.
flag_rgb() <<
">red)"
387 << (
screen_width >= 1200 ?
"~SCALE_SHARP(200%,200%)" :
""));
389 if(!female_type.
image().empty() && female_type.
image() != male_type.
image()) {
391 << female_type.
image() <<
"~RC(" << female_type.
flag_rgb() <<
">red)"
392 << (
screen_width >= 1200 ?
"~SCALE_SHARP(200%,200%)" :
""));
400 const bool first_reverse_value =
true;
401 bool reverse = first_reverse_value;
404 std::vector<std::string> adv_units =
408 for(
const std::string &adv : adv_units) {
416 ss <<
_(
"Advances from:");
418 ss <<
_(
"Advances to:");
426 std::string lang_unit =
type->type_name();
429 const std::string section_prefix =
type->show_variations_in_help() ?
".." :
"";
442 }
while(
reverse != first_reverse_value);
463 for(
const std::string& var_id : parent->
variations()) {
466 if(
type.hide_help()) {
479 std::string var_name =
type.variation_name();
497 if(race_name.empty()) {
498 race_name =
_ (
"race^Miscellaneous");
507 std::vector<trait_data> must_have_traits;
508 std::vector<trait_data> random_traits;
509 int must_have_nameless_traits = 0;
511 for(
const config& trait : traits) {
512 const std::string& male_name = trait[
"male_name"].str();
513 const std::string& female_name = trait[
"female_name"].str();
514 std::string trait_name;
516 trait_name = male_name;
518 trait_name = female_name;
519 else if(! trait[
"name"].str().empty())
520 trait_name = trait[
"name"].str();
525 if(lang_trait_name.empty() && trait[
"availability"].str() ==
"musthave") {
526 ++must_have_nameless_traits;
529 const std::string ref_id =
"traits_"+trait[
"id"].str();
530 ((trait[
"availability"].str() ==
"musthave") ? must_have_traits : random_traits).emplace_back(lang_trait_name, ref_id);
533 int nr_random_traits =
type_.
num_traits() - must_have_traits.size() - must_have_nameless_traits;
534 if(must_have_traits.empty()) {
535 if(nr_random_traits > 0) {
542 if(nr_random_traits > 0) {
543 ss <<
"\n(" << must_have_traits.size() <<
"):" <<
font::nbsp;
564 const std::string ref_id =
ability_prefix + ability.id + ability.name.base_str();
566 if(ability.name.empty()) {
590 const std::string ref_id =
ability_prefix + ability.id + ability.name.base_str();
592 if(ability.name.empty()) {
650 ss <<
"\n" << detailed_description;
653 ss <<
"\n" <<
markup::tag(
"header",
_(
"Special Notes")) <<
"\n";
654 for(
const auto& note : notes) {
659 std::stringstream table_ss;
673 bool has_special =
false;
675 if(!attack.special_tooltips().empty()) {
682 { {
"bgcolor",
"table_header"} },
694 std::stringstream attack_ss;
696 std::string lang_weapon = attack.name();
697 std::string lang_type =
string_table[
"type_" + attack.type()];
709 " ", attack.accuracy_parry_description(),
714 attack.attacks_used(),
715 { {
"num", std::to_string(attack.attacks_used())} }));
719 " ", attack.accuracy_parry_description());
723 const std::string range_icon =
"icons/profiles/" + attack.range() +
"_attack.png~SCALE_INTO(16,16)";
724 if(attack.min_range() > 1 || attack.max_range() > 1) {
727 attack.min_range(),
"-", attack.max_range(),
' ',
736 const std::string type_icon =
"icons/profiles/" + attack.type() +
".png~SCALE_INTO(16,16)";
741 std::vector<std::pair<t_string, t_string>> specials = attack.special_tooltips();
742 if(!specials.empty()) {
743 std::stringstream specials_ss;
744 std::string lang_special =
"";
745 const std::size_t specials_size = specials.size();
746 for(std::size_t
i = 0;
i != specials_size; ++
i) {
747 const std::string ref_id = std::string(
"weaponspecial_")
748 + specials[
i].first.base_str();
749 lang_special = (specials[
i].first);
751 if(
i+1 != specials_size) {
755 attack_ss <<
markup::tag(
"col", specials_ss.str());
761 table_ss <<
markup::tag(
"row", { {
"bgcolor",
"table_row1"} }, attack_ss.str());
770 movetype movement_type = type_.movement_type();
772 if(!traits.empty() && type_.num_traits() > 0) {
773 for(
const config &
t : traits) {
774 if(
t[
"availability"].str() ==
"musthave") {
775 for(
const config & effect :
t.child_range(
"effect")) {
776 if(!effect.has_child(
"filter")
778 movement_type.
merge(effect, effect[
"replace"].to_bool());
786 const bool has_vision = type_.movement_type().has_vision_data();
787 const bool has_jamming = type_.movement_type().has_jamming_data();
794 std::stringstream().swap(table_ss);
796 { {
"bgcolor",
"table_header"} },
801 for(
const auto& [damage_type, damage_resistance] : movement_type.
damage_table()) {
802 int resistance = 100;
804 resistance -=
std::stoi(damage_resistance);
805 }
catch(std::invalid_argument&) {}
806 std::string resist = std::to_string(resistance) +
'%';
807 const std::size_t pos = resist.find(
'-');
808 if(pos != std::string::npos) {
812 const std::string lang_type =
string_table[
"type_" + damage_type];
813 const std::string type_icon =
"icons/profiles/" + damage_type +
".png~SCALE_INTO(16,16)";
815 { {
"bgcolor", (odd_row ?
"table_row1" :
"table_row2")} },
826 std::stringstream().swap(table_ss);
829 ss <<
"\n" <<
markup::tag(
"header",
_(
"Terrain Modifiers"));
832 std::stringstream row_ss;
839 table_ss <<
markup::tag(
"row", { {
"bgcolor",
"table_header"} }, row_ss.str());
842 std::set<terrain_movement_info> terrain_moves;
849 const bool cannot_move = moves > type_.movement();
850 if(cannot_move &&
info.hide_if_impassable()) {
854 if(
info.is_indivisible() &&
info.is_nonnull()) {
855 terrain_movement_info movement_info =
866 terrain_moves.insert(movement_info);
872 for(
const terrain_movement_info& m : terrain_moves)
874 std::stringstream().swap(row_ss);
875 bool high_res =
false;
876 const std::string tc_base = high_res ?
"images/buttons/icon-base-32.png" :
"images/buttons/icon-base-16.png";
877 const std::string terrain_image =
"icons/terrain/terrain_type_" + m.id + (high_res ?
"_30.png" :
".png");
878 const std::string final_image = tc_base +
"~RC(magenta>" + m.id +
")~BLIT(" + terrain_image +
")";
888 row_ss <<
markup::tag(
"col", format_mp_entry(type_.movement(), m.movement_cost));
891 if(has_terrain_defense_caps) {
902 row_ss <<
markup::tag(
"col", format_mp_entry(type_.vision(), m.vision_cost));
908 row_ss <<
markup::tag(
"col", format_mp_entry(type_.jamming(), m.jamming_cost));
911 table_ss <<
markup::tag(
"row", { {
"bgcolor", (odd_row ?
"table_row1" :
"table_row2")} }, row_ss.str());
919 WRN_HP <<
"When building unit help topics, we couldn't get the terrain info we need.";
std::vector< std::string > names
A config object defines a single node in a WML file, with access to child nodes.
boost::iterator_range< const_child_iterator > const_child_itors
const terrain_type & type_
virtual std::string operator()() const
virtual std::string operator()() const
const std::string variation_
bool capped(const t_translation::terrain_code &terrain) const
Returns whether there is a defense cap associated to this terrain.
The basic "size" of the unit - flying, small land, large land, etc.
static const std::set< std::string > effects
The set of applicable effects for movement types.
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...
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.
int defense_modifier(const t_translation::terrain_code &terrain) const
Returns the defensive value of the indicated terrain.
int jamming_cost(const t_translation::terrain_code &terrain, bool slowed=false) const
Returns the cost to "jam" through 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.
int movement_cost(const t_translation::terrain_code &terrain, bool slowed=false) const
Returns the cost to move through the indicated terrain.
utils::string_map_res damage_table() const
Returns a map from damage types to resistances.
terrain_defense & get_defense()
const std::string & str() const
static std::shared_ptr< terrain_type_data > get()
const std::string & editor_group() const
bool has_default_base() const
const std::string & icon_image() const
const t_string & income_description() const
bool is_combined() const
True for instances created by the terrain_code(base, overlay) constructor.
const std::string & editor_image() const
bool is_nonnull() const
True if this object represents some sentinel values.
const std::string & id() const
const t_string & help_topic_text() const
const t_translation::ter_list & def_type() const
const t_translation::ter_list & mvt_type() const
The underlying type of the terrain.
int light_bonus(int base) const
Returns the light (lawful) bonus for this terrain when the time of day gives a base bonus.
const t_translation::ter_list & union_type() const
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,...
const t_string & editor_name() const
int gives_healing() const
t_translation::terrain_code default_base() const
Overlay terrains defined by a [terrain_type] can declare a fallback base terrain, for use when the ov...
bool hide_in_editor() const
t_translation::terrain_code number() 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.
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.
A single unit type that the player may recruit.
const std::vector< std::string > advances_from() const
A vector of unit_type ids that can advance to this unit_type.
std::string race_id() const
Returns the ID of this type's race without the need to build the type.
static std::string alignment_description(unit_alignments::type align, unit_race::GENDER gender=unit_race::MALE)
Implementation detail of unit_type::alignment_description.
const unit_type & get_gender_unit_type(const std::string &gender) const
Returns a gendered variant of this unit_type.
const std::string & image() const
const std::string & id() const
The id for this unit_type.
const std::vector< ability_metadata > & adv_abilities_metadata() const
Some extra abilities that may be gained through AMLA advancements.
bool show_variations_in_help() const
Whether the unit type has at least one help-visible variation.
const unit_race * race() const
Never returns nullptr, but may point to the null race.
const unit_type & get_variation(const std::string &id) const
const_attack_itors attacks() const
const std::vector< std::string > & advances_to() const
A vector of unit_type ids that this unit_type can advance to.
bool has_gender_variation(const unit_race::GENDER gender) const
t_string unit_description() const
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::string > variations() const
const std::string & flag_rgb() const
std::vector< t_string > special_notes() const
Returns all notes that should be displayed in the help page for this type, including those found in a...
config::const_child_itors modification_advancements() const
Returns two iterators pointing to a range of AMLA configs.
int experience_needed(bool with_acceleration=true) const
const std::string & big_profile() const
const t_string & type_name() const
The name of the unit in the current language setting.
config::const_child_itors possible_traits() const
unit_alignments::type alignment() const
const std::string & small_profile() const
const std::vector< ability_metadata > & abilities_metadata() const
const config & get_cfg() const
unsigned int num_traits() const
static std::string _(const char *str)
static lg::log_domain log_help("help")
T end(const std::pair< T, T > &p)
Standard logging facilities (interface).
EXIT_STATUS start(bool clear_id, const std::string &filename, bool take_screenshot, const std::string &screenshot_filename)
Main interface for launching the editor from the title screen.
const std::string unicode_em_dash
const std::string unicode_bullet
const std::string unicode_figure_dash
const std::string weapon_numbers_sep
const std::string unicode_minus
color_t red_to_green(double val, bool for_text)
Return a color corresponding to the value val red for val=0.0 to green for val=100....
unsigned screen_width
The screen resolution and pixel pitch should be available for all widgets since their drawing method ...
const std::string unit_prefix
const std::string variation_prefix
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 ability_prefix
std::pair< std::string, std::string > trait_data
const std::string terrain_prefix
const std::string unknown_unit_topic
static void print_trait_list(std::stringstream &ss, const std::vector< trait_data > &l)
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 bold(Args &&... data)
Applies bold Pango markup to the input.
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.
const terrain_code VOID_TERRAIN
VOID_TERRAIN is used for shrouded hexes.
bool terrain_matches(const terrain_code &src, const terrain_code &dest)
Tests whether a specific terrain matches an expression, for matching rules see above.
std::vector< terrain_code > ter_list
const ter_match ALL_OFF_MAP
const terrain_code FOGGED
static std::string gettext(const char *str)
int icompare(const std::string &s1, const std::string &s2)
Case-insensitive lexicographical comparison.
std::string resistance_color(const int resistance)
Maps resistance <= -60 (resistance value <= -60%) to intense red.
int stoi(std::string_view str)
Same interface as std::stoi and meant as a drop in replacement, except:
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
std::string format_conjunct_list(const t_string &empty, const std::vector< t_string > &elems)
Format a conjunctive list.
std::map< std::string, t_string > string_map
std::vector< std::string > split(const config_attribute_value &val)
point game_canvas_size()
The size of the game canvas, in drawing coordinates / game pixels.
The basic class for representing 8-bit RGB or RGBA colour values.
bool operator<(const terrain_movement_info &other) const
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
static map_location::direction s
unit_type_data unit_types