refactor unit_types construction
We change the code so that it now no longer changes the game_config object, this prepares a patch to disable unused addons during a game.
This commit is contained in:
parent
553b0bada6
commit
a34cb53c33
2 changed files with 258 additions and 242 deletions
|
@ -46,6 +46,7 @@ static lg::log_domain log_config("config");
|
|||
|
||||
static lg::log_domain log_unit("unit");
|
||||
#define DBG_UT LOG_STREAM(debug, log_unit)
|
||||
#define LOG_UT LOG_STREAM(info, log_unit)
|
||||
#define ERR_UT LOG_STREAM(err, log_unit)
|
||||
|
||||
/* ** unit_type ** */
|
||||
|
@ -55,6 +56,7 @@ unit_type::unit_type(const unit_type& o)
|
|||
, id_(o.id_)
|
||||
, debug_id_(o.debug_id_)
|
||||
, parent_id_(o.parent_id_)
|
||||
, base_unit_id_(o.base_unit_id_)
|
||||
, type_name_(o.type_name_)
|
||||
, description_(o.description_)
|
||||
, hitpoints_(o.hitpoints_)
|
||||
|
@ -97,14 +99,15 @@ unit_type::unit_type(const unit_type& o)
|
|||
gender_types_[1].reset(gender_types_[1] != nullptr ? new unit_type(*o.gender_types_[1]) : nullptr);
|
||||
}
|
||||
|
||||
unit_type::unit_type(const config& cfg, const std::string& parent_id)
|
||||
: cfg_(cfg)
|
||||
unit_type::unit_type(defaut_ctor_t, const config& cfg, const std::string & parent_id)
|
||||
: cfg_(nullptr)
|
||||
, built_cfg_()
|
||||
, has_cfg_build_()
|
||||
, id_(cfg_.has_attribute("id") ? cfg_["id"].str() : parent_id)
|
||||
, id_(cfg.has_attribute("id") ? cfg["id"].str() : parent_id)
|
||||
, debug_id_()
|
||||
, parent_id_(!parent_id.empty() ? parent_id : id_)
|
||||
, type_name_(cfg_["name"].t_str())
|
||||
, base_unit_id_()
|
||||
, type_name_()
|
||||
, description_()
|
||||
, hitpoints_(0)
|
||||
, hp_bar_scaling_(0.0)
|
||||
|
@ -127,7 +130,7 @@ unit_type::unit_type(const config& cfg, const std::string& parent_id)
|
|||
, gender_types_()
|
||||
, variations_()
|
||||
, default_variation_()
|
||||
, variation_name_(cfg_["variation_name"].t_str())
|
||||
, variation_name_()
|
||||
, race_(&unit_race::null_race)
|
||||
, abilities_()
|
||||
, adv_abilities_()
|
||||
|
@ -143,9 +146,26 @@ unit_type::unit_type(const config& cfg, const std::string& parent_id)
|
|||
, animations_()
|
||||
, build_status_(NOT_BUILT)
|
||||
{
|
||||
if(const config& base_unit = cfg.child("base_unit")) {
|
||||
base_unit_id_ = base_unit["id"].str();
|
||||
LOG_UT << "type '" << id_ << "' has base unit '" << base_unit_id_ << "'\n";
|
||||
}
|
||||
check_id(id_);
|
||||
check_id(parent_id_);
|
||||
}
|
||||
unit_type::unit_type(const config& cfg, const std::string & parent_id)
|
||||
: unit_type(defaut_ctor_t(), cfg, parent_id)
|
||||
{
|
||||
cfg_ = &cfg;
|
||||
|
||||
}
|
||||
|
||||
unit_type::unit_type(config&& cfg, const std::string & parent_id)
|
||||
: unit_type(defaut_ctor_t(), cfg, parent_id)
|
||||
{
|
||||
built_cfg_ = std::make_unique<config>(std::move(cfg));
|
||||
}
|
||||
|
||||
|
||||
unit_type::~unit_type()
|
||||
{
|
||||
|
@ -192,12 +212,12 @@ void unit_type::build_full(
|
|||
}
|
||||
}
|
||||
|
||||
zoc_ = cfg_["zoc"].to_bool(level_ > 0);
|
||||
zoc_ = get_cfg()["zoc"].to_bool(level_ > 0);
|
||||
|
||||
game_config::add_color_info(cfg_);
|
||||
game_config::add_color_info(get_cfg());
|
||||
|
||||
hp_bar_scaling_ = cfg_["hp_bar_scaling"].to_double(game_config::hp_bar_scaling);
|
||||
xp_bar_scaling_ = cfg_["xp_bar_scaling"].to_double(game_config::xp_bar_scaling);
|
||||
hp_bar_scaling_ = get_cfg()["hp_bar_scaling"].to_double(game_config::hp_bar_scaling);
|
||||
xp_bar_scaling_ = get_cfg()["xp_bar_scaling"].to_double(game_config::xp_bar_scaling);
|
||||
|
||||
// Propagate the build to the variations.
|
||||
for(variations_map::value_type& variation : variations_) {
|
||||
|
@ -223,33 +243,35 @@ void unit_type::build_help_index(
|
|||
// Make sure we are built to the preceding build level.
|
||||
build_created();
|
||||
|
||||
type_name_ = cfg_["name"];
|
||||
description_ = cfg_["description"];
|
||||
hitpoints_ = cfg_["hitpoints"].to_int(1);
|
||||
level_ = cfg_["level"];
|
||||
recall_cost_ = cfg_["recall_cost"].to_int(-1);
|
||||
movement_ = cfg_["movement"].to_int(1);
|
||||
vision_ = cfg_["vision"].to_int(-1);
|
||||
jamming_ = cfg_["jamming"].to_int(0);
|
||||
max_attacks_ = cfg_["attacks"].to_int(1);
|
||||
usage_ = cfg_["usage"].str();
|
||||
undead_variation_ = cfg_["undead_variation"].str();
|
||||
default_variation_ = cfg_["variation"].str();
|
||||
image_ = cfg_["image"].str();
|
||||
icon_ = cfg_["image_icon"].str();
|
||||
small_profile_ = cfg_["small_profile"].str();
|
||||
profile_ = cfg_["profile"].str();
|
||||
flag_rgb_ = cfg_["flag_rgb"].str();
|
||||
do_not_list_ = cfg_["do_not_list"].to_bool(false);
|
||||
const config& cfg = get_cfg();
|
||||
|
||||
for(const config& sn : cfg_.child_range("special_note")) {
|
||||
type_name_ = cfg["name"];
|
||||
description_ = cfg["description"];
|
||||
hitpoints_ = cfg["hitpoints"].to_int(1);
|
||||
level_ = cfg["level"];
|
||||
recall_cost_ = cfg["recall_cost"].to_int(-1);
|
||||
movement_ = cfg["movement"].to_int(1);
|
||||
vision_ = cfg["vision"].to_int(-1);
|
||||
jamming_ = cfg["jamming"].to_int(0);
|
||||
max_attacks_ = cfg["attacks"].to_int(1);
|
||||
usage_ = cfg["usage"].str();
|
||||
undead_variation_ = cfg["undead_variation"].str();
|
||||
default_variation_ = cfg["variation"].str();
|
||||
image_ = cfg["image"].str();
|
||||
icon_ = cfg["image_icon"].str();
|
||||
small_profile_ = cfg["small_profile"].str();
|
||||
profile_ = cfg["profile"].str();
|
||||
flag_rgb_ = cfg["flag_rgb"].str();
|
||||
do_not_list_ = cfg["do_not_list"].to_bool(false);
|
||||
|
||||
for(const config& sn : cfg.child_range("special_note")) {
|
||||
special_notes_.push_back(sn["note"]);
|
||||
}
|
||||
|
||||
adjust_profile(profile_);
|
||||
|
||||
alignment_ = unit_type::ALIGNMENT::NEUTRAL;
|
||||
alignment_.parse(cfg_["alignment"].str());
|
||||
alignment_.parse(cfg["alignment"].str());
|
||||
|
||||
for(int i = 0; i < 2; ++i) {
|
||||
if(gender_types_[i]) {
|
||||
|
@ -257,7 +279,11 @@ void unit_type::build_help_index(
|
|||
}
|
||||
}
|
||||
|
||||
const race_map::const_iterator race_it = races.find(cfg_["race"]);
|
||||
for(auto& pair : variations_) {
|
||||
pair.second.build_help_index(mv_types, races, traits);
|
||||
}
|
||||
|
||||
const race_map::const_iterator race_it = races.find(cfg["race"]);
|
||||
if(race_it != races.end()) {
|
||||
race_ = &race_it->second;
|
||||
} else {
|
||||
|
@ -265,9 +291,9 @@ void unit_type::build_help_index(
|
|||
}
|
||||
|
||||
// if num_traits is not defined, we use the num_traits from race
|
||||
num_traits_ = cfg_["num_traits"].to_int(race_->num_traits());
|
||||
num_traits_ = cfg["num_traits"].to_int(race_->num_traits());
|
||||
|
||||
for(const std::string& g : utils::split(cfg_["gender"])) {
|
||||
for(const std::string& g : utils::split(cfg["gender"])) {
|
||||
genders_.push_back(string_gender(g));
|
||||
}
|
||||
|
||||
|
@ -276,13 +302,13 @@ void unit_type::build_help_index(
|
|||
genders_.push_back(unit_race::MALE);
|
||||
}
|
||||
|
||||
if(const config& abil_cfg = cfg_.child("abilities")) {
|
||||
if(const config& abil_cfg = cfg.child("abilities")) {
|
||||
for(const config::any_child& ab : abil_cfg.all_children_range()) {
|
||||
abilities_.emplace_back(ab.cfg);
|
||||
}
|
||||
}
|
||||
|
||||
for(const config& adv : cfg_.child_range("advancement")) {
|
||||
for(const config& adv : cfg.child_range("advancement")) {
|
||||
for(const config& effect : adv.child_range("effect")) {
|
||||
const config& abil_cfg = effect.child("abilities");
|
||||
|
||||
|
@ -297,7 +323,7 @@ void unit_type::build_help_index(
|
|||
}
|
||||
|
||||
// Set the movement type.
|
||||
const std::string move_type = cfg_["movement_type"];
|
||||
const std::string move_type = cfg["movement_type"];
|
||||
const movement_type_map::const_iterator find_it = mv_types.find(move_type);
|
||||
|
||||
if(find_it != mv_types.end()) {
|
||||
|
@ -308,7 +334,7 @@ void unit_type::build_help_index(
|
|||
}
|
||||
|
||||
// Override parts of the movement type with what is in our config.
|
||||
movement_type_.merge(cfg_);
|
||||
movement_type_.merge(cfg);
|
||||
|
||||
for(const config& t : traits) {
|
||||
possible_traits_.add_child("trait", t);
|
||||
|
@ -319,7 +345,7 @@ void unit_type::build_help_index(
|
|||
possible_traits_.clear();
|
||||
}
|
||||
|
||||
if(cfg_["ignore_race_traits"].to_bool()) {
|
||||
if(cfg["ignore_race_traits"].to_bool()) {
|
||||
possible_traits_.clear();
|
||||
} else {
|
||||
for(const config& t : race_->additional_traits()) {
|
||||
|
@ -334,30 +360,11 @@ void unit_type::build_help_index(
|
|||
}
|
||||
|
||||
// Insert any traits that are just for this unit type
|
||||
for(const config& trait : cfg_.child_range("trait")) {
|
||||
for(const config& trait : cfg.child_range("trait")) {
|
||||
possible_traits_.add_child("trait", trait);
|
||||
}
|
||||
|
||||
for(const config& var_cfg : cfg_.child_range("variation")) {
|
||||
const std::string& var_id = var_cfg["variation_id"].empty()
|
||||
? var_cfg["variation_name"]
|
||||
: var_cfg["variation_id"];
|
||||
|
||||
variations_map::iterator ut;
|
||||
bool success;
|
||||
std::tie(ut, success) = variations_.emplace(var_id, unit_type(var_cfg, id_));
|
||||
|
||||
if(success) {
|
||||
ut->second.debug_id_ = debug_id_ + " [" + var_id + "]";
|
||||
ut->second.parent_id_ = parent_id_; // In case this is not id_.
|
||||
ut->second.variation_id_ = var_id;
|
||||
ut->second.build_help_index(mv_types, races, traits);
|
||||
} else {
|
||||
ERR_CF << "Skipping duplicate unit variation ID: " << var_id << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
hide_help_ = cfg_["hide_help"].to_bool();
|
||||
hide_help_ = cfg["hide_help"].to_bool();
|
||||
|
||||
build_status_ = HELP_INDEXED;
|
||||
}
|
||||
|
@ -374,21 +381,6 @@ void unit_type::build_created()
|
|||
return;
|
||||
}
|
||||
|
||||
// There is no preceding build level (other than being constructed).
|
||||
|
||||
// These should still be nullptr from the constructor.
|
||||
assert(!gender_types_[0]);
|
||||
assert(!gender_types_[1]);
|
||||
|
||||
if(const config& male_cfg = cfg_.child("male")) {
|
||||
gender_types_[0].reset(new unit_type(male_cfg, id_));
|
||||
gender_types_[0]->debug_id_ = debug_id_ + " (male)";
|
||||
}
|
||||
|
||||
if(const config& female_cfg = cfg_.child("female")) {
|
||||
gender_types_[1].reset(new unit_type(female_cfg, id_));
|
||||
gender_types_[1]->debug_id_ = debug_id_ + " (female)";
|
||||
}
|
||||
|
||||
for(unsigned i = 0; i < gender_types_.size(); ++i) {
|
||||
if(gender_types_[i]) {
|
||||
|
@ -396,15 +388,26 @@ void unit_type::build_created()
|
|||
}
|
||||
}
|
||||
|
||||
const std::string& advances_to_val = cfg_["advances_to"];
|
||||
for(auto& pair : variations_) {
|
||||
pair.second.build_created();
|
||||
}
|
||||
|
||||
|
||||
const config& cfg = get_cfg();
|
||||
|
||||
const std::string& advances_to_val = cfg["advances_to"];
|
||||
if(advances_to_val != "null" && !advances_to_val.empty()) {
|
||||
advances_to_ = utils::split(advances_to_val);
|
||||
}
|
||||
|
||||
|
||||
type_name_ = cfg["name"].t_str();
|
||||
variation_name_ = cfg["variation_name"].t_str();
|
||||
|
||||
DBG_UT << "unit_type '" << log_id() << "' advances to : " << advances_to_val << "\n";
|
||||
|
||||
experience_needed_ = cfg_["experience"].to_int(500);
|
||||
cost_ = cfg_["cost"].to_int(1);
|
||||
experience_needed_ = cfg["experience"].to_int(500);
|
||||
cost_ = cfg["cost"].to_int(1);
|
||||
|
||||
build_status_ = CREATED;
|
||||
}
|
||||
|
@ -499,7 +502,7 @@ const std::vector<t_string>& unit_type::special_notes() const {
|
|||
const std::vector<unit_animation>& unit_type::animations() const
|
||||
{
|
||||
if(animations_.empty()) {
|
||||
unit_animation::fill_initial_animations(animations_, cfg_);
|
||||
unit_animation::fill_initial_animations(animations_, get_cfg());
|
||||
}
|
||||
|
||||
return animations_;
|
||||
|
@ -511,7 +514,7 @@ const_attack_itors unit_type::attacks() const
|
|||
return make_attack_itors(attacks_cache_);
|
||||
}
|
||||
|
||||
for(const config& att : cfg_.child_range("attack")) {
|
||||
for(const config& att : get_cfg().child_range("attack")) {
|
||||
attacks_cache_.emplace_back(new attack_type(att));
|
||||
}
|
||||
|
||||
|
@ -555,7 +558,7 @@ int unit_type::experience_needed(bool with_acceleration) const
|
|||
|
||||
bool unit_type::has_ability_by_id(const std::string& ability) const
|
||||
{
|
||||
if(const config& abil = cfg_.child("abilities")) {
|
||||
if(const config& abil = get_cfg().child("abilities")) {
|
||||
for(const config::any_child& ab : abil.all_children_range()) {
|
||||
if(ab.cfg["id"] == ability) {
|
||||
return true;
|
||||
|
@ -570,7 +573,7 @@ std::vector<std::string> unit_type::get_ability_list() const
|
|||
{
|
||||
std::vector<std::string> res;
|
||||
|
||||
const config& abilities = cfg_.child("abilities");
|
||||
const config& abilities = get_cfg().child("abilities");
|
||||
if(!abilities) {
|
||||
return res;
|
||||
}
|
||||
|
@ -740,7 +743,7 @@ int unit_type::resistance_against(const std::string& damage_name, bool attacker)
|
|||
int resistance = movement_type_.resistance_against(damage_name);
|
||||
unit_ability_list resistance_abilities;
|
||||
|
||||
if(const config& abilities = cfg_.child("abilities")) {
|
||||
if(const config& abilities = get_cfg().child("abilities")) {
|
||||
for(const config& cfg : abilities.child_range("resistance")) {
|
||||
if(!cfg["affect_self"].to_bool(true)) {
|
||||
continue;
|
||||
|
@ -866,110 +869,6 @@ void throw_base_unit_recursion_error(const std::vector<std::string>& base_tree,
|
|||
throw config::error(ss.str());
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates the config for the unit type with id= @a key within @a all_types.
|
||||
* Throws a config::error if the unit type cannot be found.
|
||||
*/
|
||||
config& find_unit_type_config(const std::string& key, config& all_types)
|
||||
{
|
||||
config& cfg = all_types.find_child("unit_type", "id", key);
|
||||
if(cfg) {
|
||||
return cfg;
|
||||
}
|
||||
|
||||
// Bad WML!
|
||||
ERR_CF << "unit type not found: " << key << std::endl;
|
||||
ERR_CF << all_types << std::endl;
|
||||
throw config::error("unit type not found: " + key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the provided config by merging all base units into it.
|
||||
* The @a base_tree parameter is used solely for detecting and reporting
|
||||
* cycles of base units; it is no longer needed to prevent infinite loops.
|
||||
*/
|
||||
void apply_base_unit(config& ut_cfg, config& all_types, std::vector<std::string>& base_tree)
|
||||
{
|
||||
// Get a list of base units to apply.
|
||||
std::vector<std::string> base_ids;
|
||||
for(config& base : ut_cfg.child_range("base_unit")) {
|
||||
base_ids.push_back(base["id"]);
|
||||
}
|
||||
|
||||
// Nothing to do.
|
||||
if(base_ids.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the base ids for the help system.
|
||||
ut_cfg["base_ids"] = utils::join(base_ids);
|
||||
|
||||
// Clear the base units (otherwise they could interfere with the merge).
|
||||
// This has the side-effect of breaking cycles, hence base_tree is
|
||||
// merely for error detection, not error recovery.
|
||||
ut_cfg.clear_children("base_unit");
|
||||
|
||||
// Merge the base units, in order.
|
||||
for(const std::string& base_id : base_ids) {
|
||||
// Detect recursion so the WML author is made aware of an error.
|
||||
if(std::find(base_tree.begin(), base_tree.end(), base_id) != base_tree.end()) {
|
||||
throw_base_unit_recursion_error(base_tree, base_id);
|
||||
}
|
||||
|
||||
// Find the base unit.
|
||||
config& base_cfg = find_unit_type_config(base_id, all_types);
|
||||
|
||||
// Make sure the base unit has had its base units accounted for.
|
||||
base_tree.push_back(base_id);
|
||||
|
||||
apply_base_unit(base_cfg, all_types, base_tree);
|
||||
|
||||
base_tree.pop_back();
|
||||
|
||||
// Merge the base unit "under" our config.
|
||||
ut_cfg.inherit_from(base_cfg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles inheritance for configs of [male], [female], and [variation].
|
||||
* Also removes gendered children, as those serve no purpose.
|
||||
* @a default_inherit is the default value for inherit=.
|
||||
*/
|
||||
void fill_unit_sub_type(config& var_cfg, const config& parent, bool default_inherit)
|
||||
{
|
||||
if(var_cfg["inherit"].to_bool(default_inherit)) {
|
||||
var_cfg.inherit_from(parent);
|
||||
}
|
||||
|
||||
var_cfg.clear_children("male");
|
||||
var_cfg.clear_children("female");
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes [variation] tags of @a ut_cfg, handling inheritance and
|
||||
* child clearing.
|
||||
*/
|
||||
void handle_variations(config& ut_cfg)
|
||||
{
|
||||
// Most unit types do not have variations.
|
||||
if(!ut_cfg.has_child("variation")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Pull the variations out of the base unit type.
|
||||
config variations;
|
||||
variations.splice_children(ut_cfg, "variation");
|
||||
|
||||
// Handle each variation's inheritance.
|
||||
for(config& var_cfg : variations.child_range("variation")) {
|
||||
fill_unit_sub_type(var_cfg, ut_cfg, false);
|
||||
}
|
||||
|
||||
// Restore the variations.
|
||||
ut_cfg.splice_children(variations, "variation");
|
||||
}
|
||||
|
||||
const boost::regex fai_identifier("[a-zA-Z_]+");
|
||||
|
||||
template<typename MoveT>
|
||||
|
@ -1000,12 +899,121 @@ void patch_movetype(
|
|||
}
|
||||
} // unnamed namespace
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Modifies the provided config by merging all base units into it.
|
||||
* The @a base_tree parameter is used for detecting and reporting
|
||||
* cycles of base units and in particular to prevent infinite loops.
|
||||
*/
|
||||
|
||||
void unit_type_data::apply_base_unit(unit_type& type, std::vector<std::string>& base_tree)
|
||||
{
|
||||
// Nothing to do.
|
||||
if(type.base_unit_id_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Detect recursion so the WML author is made aware of an error.
|
||||
if(std::find(base_tree.begin(), base_tree.end(), type.base_unit_id_) != base_tree.end()) {
|
||||
throw_base_unit_recursion_error(base_tree, type.base_unit_id_);
|
||||
}
|
||||
|
||||
// Find the base unit.
|
||||
const unit_type_map::iterator itor = types_.find(type.base_unit_id_);
|
||||
if(itor != types_.end()) {
|
||||
|
||||
unit_type& base_type = itor->second;
|
||||
|
||||
// Make sure the base unit has had its base units accounted for.
|
||||
base_tree.push_back(type.base_unit_id_);
|
||||
|
||||
apply_base_unit(base_type, base_tree);
|
||||
|
||||
base_tree.pop_back();
|
||||
|
||||
// Merge the base unit "under" our config.
|
||||
type.writable_cfg().inherit_from(base_type.get_cfg());
|
||||
}
|
||||
else {
|
||||
ERR_CF << "[base_unit]: unit type not found: " << type.base_unit_id_ << std::endl;
|
||||
throw config::error("unit type not found: " + type.base_unit_id_);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles inheritance for configs of [male], [female], and [variation].
|
||||
* Also removes gendered children, as those serve no purpose.
|
||||
* @a default_inherit is the default value for inherit=.
|
||||
*/
|
||||
std::unique_ptr<unit_type> unit_type::create_sub_type(const config& var_cfg, bool default_inherit)
|
||||
{
|
||||
config var_copy = var_cfg;
|
||||
if(var_cfg["inherit"].to_bool(default_inherit)) {
|
||||
var_copy.inherit_from(get_cfg());
|
||||
}
|
||||
|
||||
var_copy.clear_children("male");
|
||||
var_copy.clear_children("female");
|
||||
|
||||
return std::make_unique<unit_type>(std::move(var_copy), parent_id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes [variation] tags of @a ut_cfg, handling inheritance and
|
||||
* child clearing.
|
||||
*/
|
||||
void unit_type::fill_variations()
|
||||
{
|
||||
// Most unit types do not have variations.
|
||||
if(!get_cfg().has_child("variation")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle each variation's inheritance.
|
||||
for(const config& var_cfg : get_cfg().child_range("variation")) {
|
||||
|
||||
std::unique_ptr<unit_type> var = create_sub_type(var_cfg, false);
|
||||
|
||||
var->built_cfg_->remove_children("variation", [](const config&){return true;});
|
||||
var->debug_id_ = debug_id_ + " [" + var->variation_id_ + "]";
|
||||
|
||||
variations_map::iterator ut;
|
||||
bool success;
|
||||
std::tie(ut, success) = variations_.emplace(var_cfg["variation_id"].str(), std::move(*var));
|
||||
if(!success) {
|
||||
ERR_CF << "Skipping duplicate unit variation ID: " << var_cfg["variation_id"] << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void unit_type::fill_variations_and_gender()
|
||||
{
|
||||
// Complete the gender-specific children of the config.
|
||||
if(const config& male_cfg = get_cfg().child("male")) {
|
||||
gender_types_[0] = create_sub_type(male_cfg, true);
|
||||
gender_types_[0]->fill_variations();
|
||||
}
|
||||
|
||||
if(const config& female_cfg = get_cfg().child("female")) {
|
||||
gender_types_[1] = create_sub_type(female_cfg, true);
|
||||
gender_types_[1]->fill_variations();
|
||||
}
|
||||
|
||||
// Complete the variation-defining children of the config.
|
||||
fill_variations();
|
||||
|
||||
gui2::dialogs::loading_screen::progress();
|
||||
}
|
||||
/**
|
||||
* Resets all data based on the provided config.
|
||||
* This includes some processing of the config, such as expanding base units.
|
||||
* A pointer to the config is stored, so the config must be persistent.
|
||||
*/
|
||||
void unit_type_data::set_config(config& cfg)
|
||||
void unit_type_data::set_config(const config& cfg)
|
||||
{
|
||||
DBG_UT << "unit_type_data::set_config, name: " << cfg["name"] << "\n";
|
||||
|
||||
|
@ -1097,51 +1105,32 @@ void unit_type_data::set_config(config& cfg)
|
|||
}
|
||||
}
|
||||
|
||||
// Apply base units.
|
||||
for(config& ut : cfg.child_range("unit_type")) {
|
||||
if(ut.has_child("base_unit")) {
|
||||
// Derived units must specify a new id.
|
||||
// (An error message will be emitted later if id is empty.)
|
||||
const std::string id = ut["id"];
|
||||
if(!id.empty()) {
|
||||
std::vector<std::string> base_tree(1, id);
|
||||
apply_base_unit(ut, cfg, base_tree);
|
||||
|
||||
gui2::dialogs::loading_screen::progress();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle inheritance and recording of unit types.
|
||||
for(config& ut : cfg.child_range("unit_type")) {
|
||||
std::string id = ut["id"];
|
||||
|
||||
for(const config& ut : cfg.child_range("unit_type")) {
|
||||
// Every type is required to have an id.
|
||||
std::string id = ut["id"].str();
|
||||
if(id.empty()) {
|
||||
ERR_CF << "[unit_type] with empty id=, ignoring:\n" << ut.debug();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Complete the gender-specific children of the config.
|
||||
if(config& male_cfg = ut.child("male")) {
|
||||
fill_unit_sub_type(male_cfg, ut, true);
|
||||
handle_variations(male_cfg);
|
||||
}
|
||||
|
||||
if(config& female_cfg = ut.child("female")) {
|
||||
fill_unit_sub_type(female_cfg, ut, true);
|
||||
handle_variations(female_cfg);
|
||||
}
|
||||
|
||||
// Complete the variation-defining children of the config.
|
||||
handle_variations(ut);
|
||||
|
||||
// Record this unit type.
|
||||
if(types_.emplace(id, unit_type(ut)).second) {
|
||||
LOG_CONFIG << "added " << id << " to unit_type list (unit_type_data.unit_types)\n";
|
||||
} else {
|
||||
ERR_CF << "Multiple [unit_type]s with id=" << id << " encountered." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply base units.
|
||||
for(auto& type : types_) {
|
||||
std::vector<std::string> base_tree(1, type.second.id());
|
||||
apply_base_unit(type.second, base_tree);
|
||||
|
||||
gui2::dialogs::loading_screen::progress();
|
||||
}
|
||||
|
||||
//handle [male], [female], [variation]
|
||||
for(auto& type : types_) {
|
||||
type.second.fill_variations_and_gender();
|
||||
|
||||
gui2::dialogs::loading_screen::progress();
|
||||
}
|
||||
|
@ -1170,11 +1159,7 @@ const unit_type* unit_type_data::find(const std::string& key, unit_type::BUILD_S
|
|||
|
||||
// This might happen if units of another era are requested (for example for savegames)
|
||||
if(itor == types_.end()) {
|
||||
#if 0
|
||||
for(unit_type_map::const_iterator ut = types_.begin(); ut != types_.end(); ut++) {
|
||||
DBG_UT << "Known unit_types: key = '" << ut->first << "', id = '" << ut->second.log_id() << "'\n";
|
||||
}
|
||||
#endif
|
||||
DBG_CF << "unable to find " << key << " in unit_type list (unit_type_data.unit_types)\n";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -1308,7 +1293,7 @@ void unit_type::apply_scenario_fix(const config& cfg)
|
|||
gender_types_[gender]->apply_scenario_fix(cfg);
|
||||
}
|
||||
|
||||
if(cfg_.has_child("variation")) {
|
||||
if(get_cfg().has_child("variation")) {
|
||||
// Make sure the variations are created.
|
||||
unit_types.build_unit_type(*this, VARIATIONS);
|
||||
for(auto& v : variations_) {
|
||||
|
@ -1332,12 +1317,12 @@ void unit_type_data::apply_scenario_fix(const config& cfg)
|
|||
void unit_type::remove_scenario_fixes()
|
||||
{
|
||||
advances_to_.clear();
|
||||
const std::string& advances_to_val = cfg_["advances_to"];
|
||||
const std::string& advances_to_val = get_cfg()["advances_to"];
|
||||
if(advances_to_val != "null" && !advances_to_val.empty()) {
|
||||
advances_to_ = utils::split(advances_to_val);
|
||||
}
|
||||
experience_needed_ = cfg_["experience"].to_int(500);
|
||||
cost_ = cfg_["cost"].to_int(1);
|
||||
experience_needed_ = get_cfg()["experience"].to_int(500);
|
||||
cost_ = get_cfg()["cost"].to_int(1);
|
||||
|
||||
// apply recursively to subtypes.
|
||||
for(int gender = 0; gender <= 1; ++gender) {
|
||||
|
|
|
@ -43,6 +43,10 @@ typedef std::map<std::string, movetype> movement_type_map;
|
|||
*/
|
||||
class unit_type
|
||||
{
|
||||
private:
|
||||
struct defaut_ctor_t {};
|
||||
unit_type(defaut_ctor_t, const config& cfg, const std::string & parent_id);
|
||||
|
||||
public:
|
||||
using error = unit_type_error;
|
||||
/**
|
||||
|
@ -52,7 +56,15 @@ public:
|
|||
* storage, that is, a child of unit_type_data::unit_cfg.
|
||||
*/
|
||||
explicit unit_type(const config &cfg, const std::string & parent_id="");
|
||||
/**
|
||||
* Creates a unit type for the given config, but delays its build
|
||||
* till later.
|
||||
* @note @a cfg is copied
|
||||
*/
|
||||
explicit unit_type(config && cfg, const std::string & parent_id="");
|
||||
unit_type();
|
||||
unit_type(const unit_type& o);
|
||||
unit_type(unit_type&& o) = default;
|
||||
|
||||
~unit_type();
|
||||
|
||||
|
@ -104,7 +116,7 @@ public:
|
|||
|
||||
/// Returns two iterators pointing to a range of AMLA configs.
|
||||
config::const_child_itors modification_advancements() const
|
||||
{ return cfg_.child_range("advancement"); }
|
||||
{ return get_cfg().child_range("advancement"); }
|
||||
|
||||
/**
|
||||
* Returns a gendered variant of this unit_type.
|
||||
|
@ -155,9 +167,9 @@ public:
|
|||
const std::string& icon() const { return icon_; }
|
||||
const std::string &small_profile() const { return small_profile_; }
|
||||
const std::string &big_profile() const { return profile_; }
|
||||
std::string halo() const { return cfg_["halo"]; }
|
||||
std::string ellipse() const { return cfg_["ellipse"]; }
|
||||
bool generate_name() const { return cfg_["generate_name"].to_bool(true); }
|
||||
std::string halo() const { return get_cfg()["halo"]; }
|
||||
std::string ellipse() const { return get_cfg()["ellipse"]; }
|
||||
bool generate_name() const { return get_cfg()["generate_name"].to_bool(true); }
|
||||
const std::vector<unit_animation>& animations() const;
|
||||
|
||||
const std::string& flag_rgb() const;
|
||||
|
@ -211,13 +223,13 @@ public:
|
|||
{ return possible_traits_.child_range("trait"); }
|
||||
|
||||
const config& abilities_cfg() const
|
||||
{ return cfg_.child_or_empty("abilities"); }
|
||||
{ return get_cfg().child_or_empty("abilities"); }
|
||||
|
||||
config::const_child_itors advancements() const
|
||||
{ return cfg_.child_range("advancement"); }
|
||||
{ return get_cfg().child_range("advancement"); }
|
||||
|
||||
config::const_child_itors events() const
|
||||
{ return cfg_.child_range("event"); }
|
||||
{ return get_cfg().child_range("event"); }
|
||||
|
||||
bool has_random_traits() const;
|
||||
|
||||
|
@ -246,14 +258,21 @@ public:
|
|||
bool show_variations_in_help() const;
|
||||
|
||||
/// Returns the ID of this type's race without the need to build the type.
|
||||
std::string race_id() const { return cfg_["race"]; } //race_->id(); }
|
||||
std::string race_id() const { return get_cfg()["race"]; } //race_->id(); }
|
||||
/// Never returns nullptr, but may point to the null race.
|
||||
/// Requires building to the HELP_INDEXED status to get the correct race.
|
||||
const unit_race* race() const { return race_; }
|
||||
bool hide_help() const;
|
||||
bool do_not_list() const { return do_not_list_; }
|
||||
|
||||
const config &get_cfg() const { return cfg_; }
|
||||
const config &get_cfg() const
|
||||
{
|
||||
if(built_cfg_) {
|
||||
return *built_cfg_;
|
||||
}
|
||||
assert(cfg_);
|
||||
return *cfg_;
|
||||
}
|
||||
|
||||
/// Gets resistance while considering custom WML abilities.
|
||||
/// Attention: Filters in resistance-abilities will be ignored.
|
||||
|
@ -267,11 +286,21 @@ private:
|
|||
bool resistance_filter_matches(const config& cfg,bool attacker,const std::string& damage_name, int res) const;
|
||||
|
||||
private:
|
||||
config& writable_cfg() {
|
||||
if(!built_cfg_) {
|
||||
built_cfg_ = std::make_unique<config>(*cfg_);
|
||||
}
|
||||
return *built_cfg_;
|
||||
}
|
||||
void fill_variations();
|
||||
void fill_variations_and_gender();
|
||||
std::unique_ptr<unit_type> create_sub_type(const config& var_cfg, bool default_inherit);
|
||||
|
||||
void operator=(const unit_type& o);
|
||||
|
||||
const config &cfg_;
|
||||
const config* cfg_;
|
||||
friend class unit_type_data;
|
||||
mutable config built_cfg_;
|
||||
mutable std::unique_ptr<config> built_cfg_;
|
||||
mutable bool has_cfg_build_;
|
||||
mutable attack_list attacks_cache_;
|
||||
|
||||
|
@ -279,7 +308,7 @@ private:
|
|||
std::string debug_id_; /// A suffix for id_, used when logging messages.
|
||||
std::string parent_id_; /// The id of the top ancestor of this unit_type.
|
||||
/// from [base_unit]
|
||||
std::string base_ids_;
|
||||
std::string base_unit_id_;
|
||||
t_string type_name_;
|
||||
t_string description_;
|
||||
std::vector<t_string> special_notes_;
|
||||
|
@ -347,7 +376,7 @@ public:
|
|||
const unit_type_map &types() const { return types_; }
|
||||
const race_map &races() const { return races_; }
|
||||
const config::const_child_itors traits() const { return unit_cfg_->child_range("trait"); }
|
||||
void set_config(config &cfg);
|
||||
void set_config(const config &cfg);
|
||||
|
||||
/// Finds a unit_type by its id() and makes sure it is built to the specified level.
|
||||
const unit_type *find(const std::string &key, unit_type::BUILD_STATUS status = unit_type::FULL) const;
|
||||
|
@ -371,6 +400,8 @@ private:
|
|||
|
||||
void clear();
|
||||
|
||||
void apply_base_unit(unit_type& type, std::vector<std::string>& base_tree);
|
||||
|
||||
mutable unit_type_map types_;
|
||||
movement_type_map movement_types_;
|
||||
race_map races_;
|
||||
|
|
Loading…
Add table
Reference in a new issue