The Battle for Wesnoth  1.19.9+dev
helper.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2025
3  by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 /**
17  * @file
18  * Support functions for dealing with units.
19  */
20 
21 #include "actions/create.hpp"
22 #include "formula/string_utils.hpp"
23 #include "resources.hpp"
24 #include "units/unit.hpp"
25 #include "units/helper.hpp"
26 #include "units/types.hpp"
27 #include "play_controller.hpp"
28 #include "serialization/markup.hpp"
29 #include "utils/general.hpp"
30 #include "whiteboard/manager.hpp"
31 
32 namespace unit_helper {
33 
35 {
36  return u.advances_to().size() + u.get_modification_advances().size();
37 }
38 
40 {
41  if(!u.valid()) {
42  return false;
43  }
44  return u->advances() && number_of_possible_advances(*u) > 0;
45 }
46 
47 /**
48  * Maps resistance <= -60 (resistance value <= -60%) to intense red.
49  * Maps resistance >= 60 (resistance value >= 60%) to intense green.
50  * Intermediate values are affinely mapped to the red-to-green scale,
51  * with 0 (0%) being mapped to yellow.
52  * Compare attack_info_percent_color() in reports.cpp.
53  */
54 std::string resistance_color(const int resistance)
55 {
56  // Passing false to select the more saturated red-to-green scale.
57  return game_config::red_to_green(50.0 + resistance * 5.0 / 6.0, false).to_hex_string();
58 }
59 
60 static std::string unit_level_tooltip(const int level, const std::vector<std::string> &adv_to_types, const std::vector<config> &adv_to_mods)
61 {
62  std::ostringstream tooltip;
63  tooltip << _("Level: ") << markup::bold(level) << "\n";
64  const bool has_advancements = !adv_to_types.empty() || !adv_to_mods.empty();
65  if(has_advancements) {
66  tooltip << _("Advancements:") << "\n\t";
67  if(!adv_to_types.empty())
68  tooltip << markup::bold(utils::join(adv_to_types, "\n\t"));
69  if(!adv_to_mods.empty()) {
70  if(!adv_to_types.empty())
71  tooltip << "\n\t";
72  std::vector<std::string> descriptions;
73  for(const config& adv : adv_to_mods)
74  descriptions.push_back(adv["description"].str());
75  tooltip << markup::bold(utils::join(descriptions, "\n\t"));
76  }
77  } else {
78  tooltip << _("No advancement");
79  }
80  return tooltip.str();
81 }
82 
83 std::string unit_level_tooltip(const unit &u)
84 {
86 }
87 
88 std::string unit_level_tooltip(const unit_type &type)
89 {
90  const auto mod_adv_iters = type.modification_advancements();
91  const std::vector<config> mod_advancements(mod_adv_iters.begin(), mod_adv_iters.end());
92 
93  return unit_level_tooltip(type.level(), type.advances_to(), mod_advancements);
94 }
95 
96 std::string maybe_inactive(const std::string& str, bool active)
97 {
98  return (active ? str : markup::span_color(font::INACTIVE_COLOR, str));
99 }
100 
101 std::string format_cost_string(int unit_recall_cost, bool active)
102 {
103  std::stringstream str;
104  if (active) {
105  str << markup::img("themes/gold.png") << unit_recall_cost;
106  } else {
107  str << markup::img("themes/gold.png~GS()")
108  << maybe_inactive(std::to_string(unit_recall_cost), false);
109  }
110  return str.str();
111 }
112 
113 std::string format_cost_string(int unit_recall_cost, const int team_recall_cost)
114 {
115  if(unit_recall_cost < 0) {
116  unit_recall_cost = team_recall_cost;
117  }
118 
119  std::stringstream str;
120  str << markup::img("themes/gold.png");
121 
122  if(unit_recall_cost > team_recall_cost) {
123  str << markup::span_color(font::BAD_COLOR, unit_recall_cost);
124  } else if(unit_recall_cost < team_recall_cost) {
125  str << markup::span_color(font::GREEN_COLOR, unit_recall_cost);
126  } else {
127  // Default: show cost in white font color.
128  // Should handle the unit cost = team cost case.
129  str << unit_recall_cost;
130  }
131 
132  return str.str();
133 }
134 
135 std::string format_level_string(const int level, bool recallable)
136 {
137  if(!recallable) {
138  // Same logic as when recallable, but always in inactive_color.
140  (level < 2 ? std::to_string(level) : markup::bold(level)));
141  } else if(level < 1) {
143  } else if(level == 1) {
144  return std::to_string(level);
145  } else if(level == 2) {
146  return markup::bold(level);
147  } else if(level == 3) {
148  return markup::span_color(color_t(0xe2, 0xb7, 0x76), markup::bold(level));
149  } else {
150  return markup::span_color(color_t(0xdd, 0x66, 0x00), markup::bold(level));
151  }
152 }
153 
154 std::string format_movement_string(const int moves_left, const int moves_max, bool active)
155 {
156  if (!active) {
157  return markup::span_color(font::GRAY_COLOR, moves_left, "/", moves_max);
158  } else if(moves_left == 0) {
159  return markup::span_color(font::BAD_COLOR, moves_left, "/", moves_max);
160  } else if(moves_left > moves_max) {
161  return markup::span_color(font::YELLOW_COLOR, moves_left, "/", moves_max);
162  } else {
163  return markup::span_color(font::GREEN_COLOR, moves_left, "/", moves_max);
164  }
165 }
166 
167 // TODO: Return multiple strings here, in case more than one error applies? For
168 // example, if you start AOI S5 with 0GP and recruit a Mage, two reasons apply,
169 // leader not on keep (extrarecruit=Mage) and not enough gold.
171  const std::string& type_id,
172  map_location& target_hex,
173  map_location& recruited_from,
174  team& current_team)
175 {
176  const unit_type* u_type = unit_types.find(type_id);
177  if(u_type == nullptr) {
178  return _("Internal error. Please report this as a bug! Details:\n")
179  + "unit_helper::recruit_message: u_type == nullptr for " + type_id;
180  }
181 
182  // search for the unit to be recruited in recruits
183  if(!utils::contains(actions::get_recruits(current_team.side(), target_hex), type_id)) {
184  return VGETTEXT("You cannot recruit a $unit_type_name at this time.",
185  utils::string_map{{ "unit_type_name", u_type->type_name() }});
186  }
187 
188  // TODO take a wb::future_map RAII as units_dialog does
189  int wb_gold = 0;
190  {
191  wb::future_map future;
193  ? resources::controller->get_whiteboard()->get_spent_gold_for(current_team.side())
194  : 0);
195  }
196  if(u_type->cost() > current_team.gold() - wb_gold)
197  {
198  if(wb_gold > 0)
199  // TRANSLATORS: "plan" refers to Planning Mode
200  return _("At this point in your plan, you will not have enough gold to recruit this unit.");
201  else
202  return _("You do not have enough gold to recruit this unit.");
203  }
204 
205  current_team.last_recruit();
206  const events::command_disabler disable_commands;
207 
208  {
209  wb::future_map_if_active future; /* start planned unit map scope if in planning mode */
210  std::string msg = actions::find_recruit_location(current_team.side(), target_hex, recruited_from, type_id);
211  if(!msg.empty()) {
212  return msg;
213  }
214  } // end planned unit map scope
215 
216  return {};
217 }
218 
219 }
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:158
std::shared_ptr< wb::manager > get_whiteboard() const
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:75
int side() const
Definition: team.hpp:180
int gold() const
Definition: team.hpp:181
const std::string & last_recruit() const
Definition: team.hpp:219
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.
Definition: types.cpp:1265
A single unit type that the player may recruit.
Definition: types.hpp:43
int cost() const
Definition: types.hpp:172
const t_string & type_name() const
The name of the unit in the current language setting.
Definition: types.hpp:138
This class represents a single unit of a specific type.
Definition: unit.hpp:133
Various functions related to the creation of units (recruits, recalls, and placed units).
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
static std::string _(const char *str)
Definition: gettext.hpp:93
int level() const
The current level of this unit.
Definition: unit.hpp:575
std::vector< config > get_modification_advances() const
Gets any non-typed advanced options set by modifications.
Definition: unit.cpp:1859
const advances_to_t & advances_to() const
Gets the possible types this unit can advance to on level-up.
Definition: unit.hpp:244
const std::vector< std::string > advances_to_translated() const
Gets the names of the possible types this unit can advance to on level-up.
Definition: unit.cpp:1241
std::string tooltip
Shown when hovering over an entry in the filter's drop-down list.
Definition: manager.cpp:203
std::string find_recruit_location(const int side, map_location &recruit_location, map_location &recruited_from, const std::string &unit_type)
Finds a location on which to place a unit.
Definition: create.cpp:466
const std::set< std::string > get_recruits(int side, const map_location &recruit_loc)
Gets the recruitable units from a side's leaders' personal recruit lists who can recruit on or from a...
Definition: create.cpp:60
const color_t YELLOW_COLOR
const color_t BAD_COLOR
const color_t GREEN_COLOR
const color_t GRAY_COLOR
const color_t INACTIVE_COLOR
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....
std::string img(const std::string &src, const std::string &align, bool floating)
Generates a Help markup tag corresponding to an image.
Definition: markup.cpp:31
std::string bold(Args &&... data)
Applies bold Pango markup to the input.
Definition: markup.hpp:138
std::string span_color(const color_t &color, Args &&... data)
Applies Pango markup to the input specifying its display color.
Definition: markup.hpp:87
play_controller * controller
Definition: resources.cpp:21
static std::string unit_level_tooltip(const int level, const std::vector< std::string > &adv_to_types, const std::vector< config > &adv_to_mods)
Definition: helper.cpp:60
bool will_certainly_advance(const unit_map::iterator &u)
Encapsulates the logic for deciding whether an iterator u points to a unit that can advance.
Definition: helper.cpp:39
std::string resistance_color(const int resistance)
Maps resistance <= -60 (resistance value <= -60%) to intense red.
Definition: helper.cpp:54
std::string format_level_string(const int level, bool recallable)
Definition: helper.cpp:135
t_string recruit_message(const std::string &type_id, map_location &target_hex, map_location &recruited_from, team &current_team)
Definition: helper.cpp:170
std::string format_movement_string(const int moves_left, const int moves_max, bool active)
Definition: helper.cpp:154
std::string maybe_inactive(const std::string &str, bool active)
Definition: helper.cpp:96
int number_of_possible_advances(const unit &u)
Determines the total number of available advancements (of any kind) for a given unit.
Definition: helper.cpp:34
std::string format_cost_string(int unit_recall_cost, bool active)
Definition: helper.cpp:101
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition: general.hpp:86
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
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
int moves_left
Definition: pathfind.cpp:156
The basic class for representing 8-bit RGB or RGBA colour values.
Definition: color.hpp:59
std::string to_hex_string() const
Returns the stored color in rrggbb hex format.
Definition: color.cpp:88
Encapsulates the map of the game.
Definition: location.hpp:45
bool valid() const
Definition: map.hpp:273
ONLY IF whiteboard is currently active, applies the planned unit map for the duration of the struct's...
Definition: manager.hpp:274
Applies the planned unit map for the duration of the struct's life.
Definition: manager.hpp:253
unit_type_data unit_types
Definition: types.cpp:1504