37 #include <boost/regex.hpp>
40 #define ERR_CF LOG_STREAM(err, log_config)
41 #define WRN_CF LOG_STREAM(warn, log_config)
42 #define LOG_CONFIG LOG_STREAM(info, log_config)
43 #define DBG_CF LOG_STREAM(debug, log_config)
46 #define DBG_UT LOG_STREAM(debug, log_unit)
47 #define ERR_UT LOG_STREAM(err, log_unit)
56 built_unit_cfg_(false),
58 debug_id_(o.debug_id_),
60 type_name_(o.type_name_),
61 description_(o.description_),
62 hitpoints_(o.hitpoints_),
63 hp_bar_scaling_(o.hp_bar_scaling_),
64 xp_bar_scaling_(o.xp_bar_scaling_),
66 recall_cost_(o.recall_cost_),
67 movement_(o.movement_),
70 max_attacks_(o.max_attacks_),
73 undead_variation_(o.undead_variation_),
76 small_profile_(o.small_profile_),
78 flag_rgb_(o.flag_rgb_),
79 num_traits_(o.num_traits_),
80 variations_(o.variations_),
81 default_variation_(o.default_variation_),
84 abilities_(o.abilities_),
85 adv_abilities_(o.adv_abilities_),
86 ability_tooltips_(o.ability_tooltips_),
87 adv_ability_tooltips_(o.adv_ability_tooltips_),
89 hide_help_(o.hide_help_),
90 do_not_list_(o.do_not_list_),
91 advances_to_(o.advances_to_),
92 experience_needed_(o.experience_needed_),
93 in_advancefrom_(o.in_advancefrom_),
94 alignment_(o.alignment_),
95 movement_type_(o.movement_type_),
96 possible_traits_(o.possible_traits_),
98 animations_(o.animations_),
99 build_status_(o.build_status_)
113 built_unit_cfg_(false),
114 id_(cfg_.has_attribute(
"id") ? cfg_[
"id"].str() : parent_id),
116 base_id_(!parent_id.empty() ? parent_id :
id_),
117 type_name_(cfg_[
"name"].t_str()),
120 hp_bar_scaling_(0.0),
121 xp_bar_scaling_(0.0),
131 image_(cfg_[
"image"].str()),
135 flag_rgb_(cfg_[
"flag_rgb"].str()),
139 default_variation_(cfg_[
"variation"]),
140 variation_name_(cfg_[
"variation_name"].t_str()),
146 adv_ability_tooltips_(),
149 do_not_list_(cfg_[
"do_not_list"].to_bool(false)),
151 experience_needed_(0),
152 in_advancefrom_(false),
153 alignment_(
unit_type::ALIGNMENT::NEUTRAL),
158 build_status_(NOT_BUILT)
188 for (
int i = 0;
i < 2; ++
i) {
198 if (
cfg_[
"ignore_race_traits"].to_bool() ) {
203 if (
alignment_ != unit_type::ALIGNMENT::NEUTRAL ||
t[
"id"] !=
"fearless")
221 if(alpha_blend.
empty() ==
false) {
231 for (variations_map::value_type & variation :
variations_) {
232 variation.second->build_full(mv_types, races, traits);
273 for (
int i = 0;
i < 2; ++
i) {
278 const race_map::const_iterator race_it = races.find(
cfg_[
"race"]);
279 if(race_it != races.end()) {
280 race_ = &race_it->second;
289 for(std::vector<std::string>::const_iterator
g = genders.begin();
g != genders.end(); ++
g) {
312 const config &abil_cfg = effect.
child(
"abilities");
313 if (!abil_cfg || effect[
"apply_to"] !=
"new_ability") {
328 const movement_type_map::const_iterator find_it = mv_types.find(move_type);
329 if ( find_it != mv_types.end() ) {
330 DBG_UT <<
"inheriting from movement_type '" << move_type <<
"'\n";
333 else if ( !move_type.empty() ) {
334 DBG_UT <<
"movement_type '" << move_type <<
"' not found\n";
345 const std::string& var_id = var_cfg[
"variation_id"].empty() ?
346 var_cfg[
"variation_name"] : var_cfg[
"variation_id"];
387 for (
int i = 0;
i < 2; ++
i) {
393 if(advances_to_val !=
"null" && advances_to_val !=
"")
395 DBG_UT <<
"unit_type '" <<
log_id() <<
"' advances to : " << advances_to_val <<
"\n";
408 DBG_UT <<
"Building unit type " <<
log_id() <<
", level " << status <<
'\n';
431 ERR_UT <<
"Build of unit_type to unrecognized status (" << status <<
") requested." << std::endl;
448 const size_t i = gender;
459 const variations_map::const_iterator
i =
variations_.find(
id);
470 return (
_(
"No description available."));
486 std::vector<attack_type>
res;
496 int experience_modifier = 100;
501 experience_modifier = modifier;
511 return experience_modifier;
516 if(with_acceleration) {
538 if (ab.cfg[
"id"] == ability)
547 std::vector<std::string>
res;
550 if (!abilities)
return res;
575 << to_unit.
log_id() <<
" already known, ignoring.\n";
586 <<
" to " << xp <<
" due to (first) [advancefrom] of "
587 << to_unit.
log_id() <<
"\n";
592 <<
" to " << xp <<
" due to (multiple, lower) [advancefrom] of "
593 << to_unit.
log_id() <<
"\n";
597 <<
" to " << xp <<
" due to (multiple, higher) [advancefrom] of "
598 << to_unit.
log_id() <<
"\n";
602 for(
int gender=0; gender<=1; ++gender) {
605 WRN_CF << to_unit.
log_id() <<
" does not support gender " << gender << std::endl;
608 LOG_CONFIG <<
"gendered advancement " << gender <<
": ";
623 v->second->add_advancement(to_unit,xp);
635 if (tree.insert(adv).second) {
644 std::set<std::string> tree;
654 std::vector<std::string> adv_from;
655 for (
const unit_type_data::unit_type_map::value_type &ut :
unit_types.
types())
657 for (
const std::string& adv : ut.second.advances_to()) {
659 adv_from.push_back(ut.second.id());
672 bool current_status =
false;
680 if (mod[
"availability"] !=
"musthave")
693 if(
std::find(types.begin(), types.end(),
id()) == types.end())
698 if (effect[
"apply_to"] !=
"status") {
701 if (effect[
"add"] == status_name) {
702 current_status =
true;
704 if (effect[
"remove"] == status_name) {
705 current_status =
false;
710 return current_status;
717 while(t.first != t.second) {
719 if(availability.
blank())
return true;
720 if(strcmp(availability.
str().c_str(),
"musthave") != 0)
return true;
728 std::vector<std::string> retval;
731 retval.push_back(
val.first);
744 assert(
val.second !=
nullptr);
745 if (!
val.second->hide_help()) {
764 static char const *unit_type_attrs[] = {
"attacks",
"base_ids",
"die_sound",
765 "experience",
"flies",
"healed_sound",
"hide_help",
"hitpoints",
766 "id",
"ignore_race_traits",
"inherit",
"movement",
"movement_type",
767 "name",
"num_traits",
"variation_id",
"variation_name",
"recall_cost",
768 "cost",
"level",
"gender",
"flag_rgb",
"alignment",
"advances_to",
"do_not_list"
770 for (
const char *attr : unit_type_attrs) {
783 if (!cfg[
"affect_self"].to_bool(
true)) {
792 if (!resistance_abilities.
empty()) {
795 resistance_abilities.
highest(
"max_value").first);
802 if(!(cfg[
"active_on"].empty() || (attacker && cfg[
"active_on"]==
"offense") || (!attacker && cfg[
"active_on"]==
"defense"))) {
806 if(!apply_to.empty()) {
807 if(damage_name != apply_to) {
808 if ( apply_to.find(
',') != std::string::npos &&
809 apply_to.find(damage_name) != std::string::npos ) {
810 const std::vector<std::string>& vals =
utils::split(apply_to);
811 if(
std::find(vals.begin(),vals.end(),damage_name) == vals.end()) {
826 (LAWFUL,
N_(
"female^lawful"))
827 (FEMALE_NEUTRAL,
N_(
"female^neutral"))
828 (CHAOTIC,
N_(
"female^chaotic"))
829 (LIMINAL,
N_(
"female^liminal"))
835 assert(align.valid());
839 str = fem.to_string();
841 str = align.to_string();
854 hide_help_all_(false),
869 void throw_base_unit_recursion_error(
870 const std::vector<std::string> & base_tree,
const std::string & base_id)
872 std::stringstream ss;
873 ss <<
"[base_unit] recursion loop in [unit_type] ";
877 ERR_CF << ss.str() <<
'\n';
892 ERR_CF <<
"unit type not found: " << key << std::endl;
893 ERR_CF << all_types << std::endl;
902 void apply_base_unit(
config & ut_cfg,
config & all_types,
903 std::vector<std::string> &base_tree)
906 std::vector<std::string> base_ids;
908 base_ids.push_back(base[
"id"]);
910 if ( base_ids.empty() )
925 if (
std::find(base_tree.begin(), base_tree.end(), base_id) != base_tree.end() )
926 throw_base_unit_recursion_error(base_tree, base_id);
929 config & base_cfg = find_unit_type_config(base_id, all_types);
931 base_tree.push_back(base_id);
932 apply_base_unit(base_cfg, all_types, base_tree);
933 base_tree.pop_back();
945 void fill_unit_sub_type(
config & var_cfg,
const config & parent,
946 bool default_inherit)
948 if ( var_cfg[
"inherit"].to_bool(default_inherit) ) {
959 void handle_variations(
config & ut_cfg)
971 fill_unit_sub_type(var_cfg, ut_cfg,
false);
977 const boost::regex fai_identifier(
"[a-zA-Z_]+");
979 template<
typename MoveT>
981 config temp_cfg, original_cfg;
982 mt.write(original_cfg);
983 if (!replace && !original_cfg[new_key].blank()) {
989 boost::sregex_iterator
m(formula_str.begin(), formula_str.end(), fai_identifier);
990 for (
const boost::sregex_iterator::value_type&
p : std::make_pair(
m, boost::sregex_iterator())) {
992 variant val(original_cfg[var_name].to_int(default_val));
993 original.
add(var_name,
val);
995 temp_cfg[new_key] = formula(original);
996 mt.merge(temp_cfg,
true);
1007 DBG_UT <<
"unit_type_data::set_config, name: " << cfg[
"name"] <<
"\n";
1021 races_.insert(std::pair<std::string,unit_race>(race.
id(),
race));
1035 patch_movetype(
movement_types_[mt].get_resistances(), dmg_type, attr.second, 100,
true);
1037 if (
r.has_attribute(
"default")) {
1040 if (
r.has_attribute(mt.first)) {
1043 patch_movetype(mt.second.get_resistances(), dmg_type,
r[
"default"], 100,
false);
1053 static const std::string terrain_info_tags[] = {
"movement",
"vision",
"jamming",
"defense"};
1054 for (
const std::string &tag : terrain_info_tags) {
1064 if (tag ==
"defense") {
1065 patch_movetype(
movement_types_[mt].get_defense(), ter_type, attr.second, 100,
true);
1066 }
else if (tag ==
"vision") {
1067 patch_movetype(
movement_types_[mt].get_vision(), ter_type, attr.second, 99,
true);
1068 }
else if (tag ==
"movement") {
1069 patch_movetype(
movement_types_[mt].get_movement(), ter_type, attr.second, 99,
true);
1070 }
else if (tag ==
"jamming") {
1071 patch_movetype(
movement_types_[mt].get_jamming(), ter_type, attr.second, 99,
true);
1080 if (tag ==
"defense") {
1081 patch_movetype(mt.second.get_defense(), ter_type, info[
"default"], 100,
false);
1082 }
else if (tag ==
"vision") {
1083 patch_movetype(mt.second.get_vision(), ter_type, info[
"default"], 99,
false);
1084 }
else if (tag ==
"movement") {
1085 patch_movetype(mt.second.get_movement(), ter_type, info[
"default"], 99,
false);
1086 }
else if (tag ==
"jamming") {
1087 patch_movetype(mt.second.get_jamming(), ter_type, info[
"default"], 99,
false);
1097 if ( ut.has_child(
"base_unit") ) {
1101 if ( !
id.empty() ) {
1102 std::vector<std::string> base_tree(1,
id);
1103 apply_base_unit(ut, cfg, base_tree);
1115 ERR_CF <<
"[unit_type] with empty id=, ignoring:\n" << ut.debug();
1121 fill_unit_sub_type(male_cfg, ut,
true);
1122 handle_variations(male_cfg);
1125 fill_unit_sub_type(female_cfg, ut,
true);
1126 handle_variations(female_cfg);
1130 handle_variations(ut);
1134 LOG_CONFIG <<
"added " <<
id <<
" to unit_type list (unit_type_data.unit_types)\n";
1136 ERR_CF <<
"Multiple [unit_type]s with id=" <<
id <<
" encountered." << std::endl;
1157 if (key.empty() || key ==
"random")
return nullptr;
1159 DBG_CF <<
"trying to find " << key <<
" in unit_type list (unit_type_data.unit_types)\n";
1163 if (itor ==
types_.end()){
1174 return &itor->second;
1232 std::vector<std::string> trees =
utils::split(cfg[
"type_adv_tree"]);
1236 if (ut !=
types_.end()) {
1237 std::set<std::string> adv_tree = ut->second.advancement_tree();
1254 for (; lvl < lvl_nb; ++lvl) {
1268 int xp = af[
"experience"];
1272 if (from_unit ==
types_.end()) {
1273 std::ostringstream
msg;
1274 msg <<
"unit type '" << from <<
"' not found when resolving [advancefrom] tag for '"
1275 << to_unit.
log_id() <<
"'";
1280 from_unit->second.add_advancement(to_unit, xp);
1282 DBG_UT <<
"Added advancement ([advancefrom]) from " << from <<
" to " << to_unit.
log_id() <<
"\n";
1288 race_map::const_iterator
i =
races_.find(key);
1289 return i !=
races_.end() ? &i->second :
nullptr;
1294 assert(!
id.empty());
1297 throw error(
"Found unit type id with a leading whitespace \"" +
id +
"\"");
1299 bool gave_wanrning =
false;
1300 for (
size_t pos = 0;
pos <
id.size(); ++
pos) {
1301 const char c =
id[
pos];
1304 if (!gave_wanrning) {
1305 ERR_UT <<
"Found unit type id with invalid chracters: \"" <<
id <<
"\"\n";
1306 gave_wanrning =
true;
1322 static const std::string path_adjust =
"/transparent";
1323 const std::string::size_type
offset = profile.find_last_of(
'/', profile.find(
'~'));
1326 if(profile.find(path_adjust) != std::string::npos && offset != std::string::npos) {
1328 profile.replace(profile.find(path_adjust), path_adjust.length(),
"");
1335 offset != std::string::npos ?
1336 temp.insert(offset, path_adjust) : temp = path_adjust + temp;
~unit_experience_accelerator()
child_itors child_range(const std::string &key)
void set_config(config &cfg)
Resets all data based on the provided config.
double to_double(double def=0.) const
void remove_attribute(const std::string &key)
static void advancement_tree_internal(const std::string &id, std::set< std::string > &tree)
unit_type::BUILD_STATUS build_status_
static const std::string s_male
Standard string id (not translatable) for MALE.
void read_hide_help(const config &cfg)
Parses the [hide_help] tag.
bool uses_global_traits() const
static int get_acceleration()
void append_attributes(const config &cfg)
Adds attributes from cfg.
const std::string log_id() const
A variant on id() that is more descriptive, for use with message logging.
bool has_variation(const std::string &variation_id) const
bool hide_help(const std::string &type_id, const std::string &race_id) const
Checks if the [hide_help] tag contains these IDs.
const unit_type & get_gender_unit_type(std::string gender) const
const std::vector< t_string > & abilities() const
const std::vector< std::string > advances_from() const
std::set< std::string > advancement_tree() const
Get the advancement tree Build a set of unit type's id of this unit type's advancement tree...
const std::string & undead_variation() const
std::vector< t_string > adv_abilities_
bool musthave_status(const std::string &status) const
GLuint GLuint GLsizei GLenum type
static l_noret error(LoadState *S, const char *why)
std::string base_id_
A suffix for id_, used when logging messages.
bool has_ability_by_id(const std::string &ability) const
bool file_exists() const
Tests whether the file the locater points at exists.
attribute_map::value_type attribute
std::vector< std::string > get_ability_list() const
surface image_
The image is cached in this surface.
const std::string & id() const
GLuint const GLfloat * val
void build_all(unit_type::BUILD_STATUS status)
Makes sure the all unit_types are built to the specified level.
unit_type_data unit_types
BUILD_STATUS
Records the status of the lazy building of unit types.
std::pair< int, map_location > highest(const std::string &key, int def=0) const
const std::vector< unit_animation > & animations() const
t_string type_name_
The id of the top ancestor of this unit_type.
bool built_unit_cfg_
Generated as needed via get_cfg_for_units().
variations_map variations_
void merge(const config &new_cfg, bool overwrite=true)
Merges the given config over the existing data.
The basic "size" of the unit - flying, small land, large land, etc.
static config unit_type(const unit *u)
void clear_children(const std::string &key)
bool has_random_traits() const
bool empty() const
Tests for an attribute that either was never set or was set to "".
void build_help_index(const movement_type_map &movement_types, const race_map &races, const config::const_child_itors &traits)
Partially load data into an empty unit_type (build to HELP_INDEXED).
static lg::log_domain log_config("config")
GLsizei GLenum GLenum * types
std::pair< const_child_iterator, const_child_iterator > const_child_itors
Variant for storing WML attributes.
const config::const_child_itors & additional_traits() const
bool filter_base_matches(const config &cfg, int def)
bool blank() const
Tests for an attribute that was never set.
static const std::string s_female
Standard string id (not translatable) for FEMALE.
void inherit_from(const config &c)
Merge config 'c' into this config, preserving this config's values.
bool has_child(const std::string &key) const
Determine whether a config has a child or not.
const config & build_unit_cfg() const
Generates (and returns) a trimmed config suitable for use with units.
static UNUSEDNOWARN std::string _(const char *str)
const unit_race * find_race(const std::string &) const
void splice_children(config &src, const std::string &key)
Moves all the children with tag key from src to this.
movement_type_map movement_types_
static const unit_race null_race
Dummy race used when a race is not yet known.
const unit_type_map & types() const
std::vector< t_string > adv_ability_tooltips_
std::vector< std::set< std::string > > hide_help_type_
void add_color_info(const config &v)
std::vector< std::set< std::string > > hide_help_race_
all_children_itors all_children_range() const
In-order iteration over all children.
std::vector< unit_animation > animations_
std::string small_profile_
void add_advancement(unit_type &to_unit) const
bool resistance_filter_matches(const config &cfg, bool attacker, const std::string &damage_name, int res) const
Identical to unit::resistance_filter_matches.
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...
bool show_variations_in_help() const
Whether the unit type has at least one help-visible variation.
unit_race::GENDER string_gender(const std::string &str, unit_race::GENDER def)
std::string undead_variation_
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.
void add_advancement(const unit_type &advance_to, int experience)
Adds an additional advancement path to a unit type.
config & add_child(const std::string &key)
std::map< std::string, movetype > movement_type_map
static const map_location & null_location()
Error used for any general game error, e.g.
static const ::config * terrain
The terrain used to create the cache.
Templates and utility-routines for strings and numbers.
static lg::log_domain log_unit("unit")
void check_types(const std::vector< std::string > &types) const
GLuint GLuint GLsizei count
unit_type * gender_types_[2]
std::string join(T const &v, const std::string &s=",")
Generates a new string joining container items in a list.
std::pair< unit_type_map::iterator, bool > insert(const std::pair< std::string, unit_type > &utype)
void build_created(const movement_type_map &movement_types, const race_map &races, const config::const_child_itors &traits)
Load the most needed data into an empty unit_type (build to CREATE).
void build(BUILD_STATUS status, const movement_type_map &movement_types, const race_map &races, const config::const_child_itors &traits)
Performs a build of this to the indicated stage.
const std::vector< std::string > & advances_to() const
static void progress(const char *stage_name=nullptr)
std::map< std::string, tfilter >::iterator itor
int resistance_against(const attack_type &attack) const
Returns the resistance against the indicated attack.
void build_full(const movement_type_map &movement_types, const race_map &races, const config::const_child_itors &traits)
Load data into an empty unit_type (build to FULL).
const unit_type & get_variation(const std::string &id) const
std::pair< const config *, map_location > unit_ability
The things contained within a unit_ability_list.
config::const_child_itors possible_traits() const
const_attr_itors attribute_range() const
std::vector< std::string > advances_to_
const race_map & races() const
std::vector< std::string > variations() const
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
bool hide_help_all_
True if [hide_help] contains a 'all=yes' at its root.
static void fill_initial_animations(std::vector< unit_animation > &animations, const config &cfg)
GLdouble GLdouble GLdouble r
const config & get_cfg() const
fixed_t alpha_
Never nullptr, but may point to the null race.
unit_experience_accelerator(int modifier)
std::string replace(std::string str, const std::string &src, const std::string &dst)
Replace all instances of src in str with dst.
GLuint const GLchar * name
int experience_needed(bool with_acceleration=true) const
std::vector< t_string > abilities_
MAKE_ENUM(ALIGNMENT_FEMALE_VARIATION,(LAWFUL, N_("female^lawful"))(FEMALE_NEUTRAL, N_("female^neutral"))(CHAOTIC, N_("female^chaotic"))(LIMINAL, N_("female^liminal"))) std
Implementation detail of unit_type::alignment_description.
bool has_attribute(const std::string &key) const
BUILD_STATUS build_status_
bool find(E event, F functor)
Tests whether an event handler is available.
unit_type(const config &cfg, const std::string &parent_id="")
Creates a unit type for the given config, but delays its build till later.
static UNUSEDNOWARN std::string sgettext(const char *str)
config & child(const std::string &key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
std::vector< attack_type > attacks() const
config & find_child(const std::string &key, const std::string &name, const std::string &value)
Returns the first child of tag key with a name attribute containing value.
void adjust_profile(std::string &profile)
Standard logging facilities (interface).
static void check_id(std::string &id)
unsigned int num_traits() 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.
std::vector< std::string > split(std::string const &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
void push_back(const unit_ability &ability)
int get_composite_value() const
std::vector< t_string > ability_tooltips_
unsigned int num_traits() const
std::map< std::string, unit_race > race_map
A config object defines a single node in a WML file, with access to child nodes.
bool file_exists(const std::string &name)
Returns true if a file or directory with such name already exists.
Defines the MAKE_ENUM macro.
GLsizei const GLcharARB ** string
int resistance_against(const std::string &damage_name, bool attacker) const
Gets resistance while considering custom WML abilities.
const std::string & id() const
The id for this unit_type.
std::vector< unit_race::GENDER > genders_
t_string unit_description() const
#define ftofxp(x)
IN: float or int - OUT: fixed_t.
const std::string valid
Little parts of regex templates used to parse Wml annoations.