some fixes and improvements to the way units are stored, unstored,

...and the internal methods where modifictions are applied
This commit is contained in:
Patrick Parker 2008-06-29 11:32:10 +00:00
parent c1416fcb0b
commit 4b11e458d7
5 changed files with 102 additions and 154 deletions

View File

@ -6,18 +6,23 @@ Version 1.5.1+svn:
so they work correctly with a variant_map type
* language and i18n:
* updated translations: Finnish, German, Greek, Lithuanian, Serbian
* miscellaneous and bug fixes:
* rewrote the textbox history saving of the new widget library. This rewrite
* WML engine:
* When examining stored units, now the attacks, max_hitpoints, max_moves,
and max_experience are the "real" values and can also be modified.
* Enhanced recursion prevention for [kill] fire_event=yes [/kill]
* GUI improvements:
* Rewrote the textbox history saving of the new widget library. This rewrite
is incompatible with the old version, but since the library is still in
development, no compatibility layer has been added.
* Added gui to choose where server binary is
* Added type-a-head search to file chooser dialog
* Fixed droiding not to make wesnoth think player is observer (bug: #9675)
* Removed persistent from team configuration (bug: #10916)
* multiplayer:
* Added support for multiple sides per client in MP start
* Added support for reserving slots when reloading game
* Fixed timer end warning not to play in opponents turn (bug: #11517)
* Added recursion preventarion to [kill] fire_event=yes [/kill]
* miscellaneous and bug fixes:
* Fixed droiding not to make wesnoth think player is observer (bug: #9675)
* Removed persistance from team configuration (bug: #10916)
Version 1.5.1:
* campaigns:

View File

@ -216,26 +216,26 @@ team_num_(team), unit_count_(5,0)
<< COLUMN_SEPARATOR << _("This Turn");
items.push_back(str.str());
}
statistics_dialog::make_damage_line(items, _("Inflicted"),
stats_.damage_inflicted,
statistics_dialog::make_damage_line(items, _("Inflicted"),
stats_.damage_inflicted,
stats_.expected_damage_inflicted,
stats_.turn_damage_inflicted,
stats_.turn_expected_damage_inflicted);
statistics_dialog::make_damage_line(items, _("Taken"),
stats_.damage_taken,
statistics_dialog::make_damage_line(items, _("Taken"),
stats_.damage_taken,
stats_.expected_damage_taken,
stats_.turn_damage_taken,
stats_.turn_expected_damage_taken);
items.push_back("New stats:");
statistics_dialog::make_damage_line(items, _("Inflicted"),
stats_.damage_inflicted,
statistics_dialog::make_damage_line(items, _("Inflicted"),
stats_.damage_inflicted,
stats_.new_expected_damage_inflicted,
stats_.turn_damage_inflicted,
stats_.new_turn_expected_damage_inflicted);
statistics_dialog::make_damage_line(items, _("Taken"),
stats_.damage_taken,
statistics_dialog::make_damage_line(items, _("Taken"),
stats_.damage_taken,
stats_.new_expected_damage_taken,
stats_.turn_damage_taken,
stats_.new_turn_expected_damage_taken);
@ -246,11 +246,11 @@ statistics_dialog::~statistics_dialog()
{
}
void statistics_dialog::make_damage_line(std::vector<std::string>& items,
void statistics_dialog::make_damage_line(std::vector<std::string>& items,
const std::string& header,
const long long& damage,
const long long& damage,
const long long& expected,
const long long& turn_damage,
const long long& turn_damage,
const long long& turn_expected)
{
const int dsa = statistics::stats::desimal_shift * damage
@ -2137,7 +2137,7 @@ private:
static void set_show_unavailable(bool value)
{
show_unavailable_ = value;
}
}
//this is display-only
static void set_cmd_prefix(std::string value)
{
@ -2924,7 +2924,7 @@ private:
void console_handler::do_buff() {
const unit_map::iterator i = menu_handler_.current_unit(mouse_handler_);
if(i != menu_handler_.units_.end()) {
i->second.add_trait(get_data());
//i->second.add_trait(get_data());
menu_handler_.gui_->invalidate(i->first);
menu_handler_.gui_->invalidate_unit();
} else {

View File

@ -19,6 +19,7 @@
#include "global.hpp"
#include "foreach.hpp"
#include "game_config.hpp"
#include "game_errors.hpp"
#include "game_preferences.hpp"
@ -89,11 +90,6 @@ void sort_units(std::vector< unit > &units)
// Copy constructor
unit::unit(const unit& o):
cfg_(o.cfg_),
movement_b_(o.movement_b_),
defense_b_(o.defense_b_),
resistance_b_(o.resistance_b_),
abilities_b_(o.abilities_b_),
advances_to_(o.advances_to_),
type_(o.type_),
race_(o.race_),
@ -106,10 +102,8 @@ unit::unit(const unit& o):
hit_points_(o.hit_points_),
max_hit_points_(o.max_hit_points_),
max_hit_points_b_(o.max_hit_points_b_),
experience_(o.experience_),
max_experience_(o.max_experience_),
max_experience_b_(o.max_experience_b_),
level_(o.level_),
alignment_(o.alignment_),
flag_rgb_(o.flag_rgb_),
@ -128,7 +122,6 @@ unit::unit(const unit& o):
movement_(o.movement_),
max_movement_(o.max_movement_),
max_movement_b_(o.max_movement_b_),
movement_costs_(o.movement_costs_),
defense_mods_(o.defense_mods_),
hold_position_(o.hold_position_),
@ -147,7 +140,6 @@ unit::unit(const unit& o):
role_(o.role_),
ai_special_(o.ai_special_),
attacks_(o.attacks_),
attacks_b_(o.attacks_b_),
facing_(o.facing_),
traits_description_(o.traits_description_),
@ -265,7 +257,7 @@ unit::unit(unit_map* unitmap, const gamemap* map,
attacks_left_ = 0;
experience_ = 0;
cfg_["upkeep"]="full";
advance_to(&t->get_gender_unit_type(gender_));
advance_to(t);
if(dummy_unit == false) validate_side(side_);
if(use_traits) {
// Units that don't have traits generated are just
@ -274,6 +266,7 @@ unit::unit(unit_map* unitmap, const gamemap* map,
name_ = generate_name();
}
generate_traits(!use_traits);
reset_modifications();
apply_modifications();
set_underlying_id();
@ -298,7 +291,7 @@ unit::unit(const unit_type* t, int side, bool use_traits, bool dummy_unit, unit_
attacks_left_ = 0;
experience_ = 0;
cfg_["upkeep"]="full";
advance_to(&t->get_gender_unit_type(gender_));
advance_to(t);
if(dummy_unit == false) validate_side(side_);
if(use_traits) {
// Units that don't have traits generated are just
@ -307,6 +300,7 @@ unit::unit(const unit_type* t, int side, bool use_traits, bool dummy_unit, unit_
name_ = generate_name();
}
generate_traits(!use_traits);
reset_modifications();
apply_modifications();
set_underlying_id();
@ -359,13 +353,6 @@ void unit::set_game_context(unit_map* unitmap, const gamemap* map, const gamesta
game_events::add_events(cfg_.get_children("event"),type_);
}
void unit::add_trait(std::string /*trait*/)
{
//modifications_.add_child("trait", cfg);
apply_modifications();
}
// Apply mandatory traits (e.g. undead, mechanical) to a unit and then
// fill out with avaiable (leaders have a restircted set of available traits)
// traits until no more are available or the unit has its maximum number
@ -468,15 +455,10 @@ void unit::advance_to(const unit_type* t, bool use_traits, game_state* state)
{
t = &t->get_gender_unit_type(gender_).get_variation(variation_);
reset_modifications();
// Remove old animations
cfg_.clear_children("animation");
cfg_.clear_children("attack");
cfg_.clear_children("abilities");
// Clear cache of movement costs and defense mods
movement_costs_.clear();
defense_mods_.clear();
if(t->movement_type().get_parent()) {
cfg_.merge_with(t->movement_type().get_parent()->get_cfg());
}
@ -484,8 +466,7 @@ void unit::advance_to(const unit_type* t, bool use_traits, game_state* state)
std::string specific_profile;
if (type() != NULL) {
const std::string profile = cfg_["profile"];
if (!profile.empty() &&
profile != type()->get_gender_unit_type(gender_).get_variation(variation_).cfg_["profile"]){
if (!profile.empty() && profile != type()->cfg_["profile"]){
specific_profile = profile;
}
}
@ -521,7 +502,6 @@ void unit::advance_to(const unit_type* t, bool use_traits, game_state* state)
flag_rgb_ = t->flag_rgb();
backup_state();
bool do_heal = false; // Track whether unit should get fully healed.
@ -570,12 +550,16 @@ const unit_type* unit::type() const
{
std::map<std::string,unit_type>::const_iterator i = unit_type_data::types().find(type_id());
if(i != unit_type_data::types().end()) {
return &i->second;
return &i->second.get_gender_unit_type(gender_).get_variation(variation_);
}
if (!type_id().empty()) {
LOG_STREAM(err, engine) << "type not found for nonempty unit " << type_id() << ", returning NULL!\n";
std::string error_message = _("Unknown unit type '$type|'");
utils::string_map symbols;
symbols["type"] = type_id();
error_message = utils::interpolate_variables_into_string(error_message, &symbols);
LOG_STREAM(err, engine) << "unit of type " << type_id() << " not found!\n";
throw game::game_error(error_message);
}
return NULL;
}
@ -700,6 +684,7 @@ void unit::new_level()
remove_temporary_modifications();
// Re-apply all permanent modifications
reset_modifications();
apply_modifications();
heal_all();
@ -1149,15 +1134,6 @@ void unit::read(const config& cfg, bool use_traits, game_state* state)
if(cfg["type"].empty()) {
throw game::load_game_failed("Attempt to de-serialize a unit with no 'type' field (probably empty)");
}
std::map<std::string,unit_type>::const_iterator uti = unit_type_data::types().find(cfg["type"]);
if(uti == unit_type_data::types().end()) {
std::string error_message = _("Unknown unit type '$type|'");
utils::string_map symbols;
symbols["type"] = cfg["type"];
error_message = utils::interpolate_variables_into_string(error_message, &symbols);
LOG_STREAM(err, engine) << "unit of type " << cfg["type"] << " not found!\n";
throw game::game_error(error_message);
}
type_ = cfg["type"];
cfg_ = cfg;
@ -1171,13 +1147,8 @@ void unit::read(const config& cfg, bool use_traits, game_state* state)
// Prevent un-initialized variables
hit_points_=1;
// Collect these early so they can be modified by traits.
max_hit_points_ = lexical_cast_default<int>(cfg["max_hitpoints"], 1);
max_movement_ = lexical_cast_default<int>(cfg["max_moves"]);
max_experience_ = lexical_cast_default<int>(cfg["max_experience"]);
if(cfg["gender"].empty()) {
gender_ = generate_gender(uti->second, utils::string_bool(cfg_["random_gender"], false), state);
gender_ = generate_gender(*type(), utils::string_bool(cfg_["random_gender"], false), state);
} else {
gender_ = string_gender(cfg["gender"]);
}
@ -1246,7 +1217,7 @@ void unit::read(const config& cfg, bool use_traits, game_state* state)
cfg_.remove_child("modifications",0);
}
advance_to(&uti->second.get_gender_unit_type(gender_), use_traits, state);
advance_to(type(), use_traits, state);
if(cfg["race"] != "") {
const race_map::const_iterator race_it = unit_type_data::types().races().find(cfg["race"]);
if(race_it != unit_type_data::types().races().end()) {
@ -1276,6 +1247,9 @@ void unit::read(const config& cfg, bool use_traits, game_state* state)
if(cfg["profile"] != "") {
cfg_["profile"] = cfg["profile"];
}
max_hit_points_ = lexical_cast_default<int>(cfg["max_hitpoints"], max_hit_points_);
max_movement_ = lexical_cast_default<int>(cfg["max_moves"], max_movement_);
max_experience_ = lexical_cast_default<int>(cfg["max_experience"], max_experience_);
//support for unit formulas and unit-specyfic variables in [ai_vars]
unit_formula_ = cfg["formula"];
@ -1300,11 +1274,10 @@ void unit::read(const config& cfg, bool use_traits, game_state* state)
//dont use the unit_type's attacks if this config has its own defined
config::const_child_itors attack_cfg_range = cfg.child_range("attack");
if(attack_cfg_range.first != attack_cfg_range.second) {
attacks_b_.clear();
attacks_.clear();
do {
attacks_b_.push_back(attack_type(**attack_cfg_range.first));
attacks_.push_back(attack_type(**attack_cfg_range.first));
} while(++attack_cfg_range.first != attack_cfg_range.second);
apply_modifications();
}
cfg_.clear_children("attack");
@ -1388,14 +1361,6 @@ void unit::write(config& cfg) const
std::string x = cfg["x"];
std::string y = cfg["y"];
cfg.append(cfg_);
cfg.clear_children("movement_costs");
cfg.clear_children("defense");
cfg.clear_children("resistance");
cfg.clear_children("abilities");
cfg.add_child("movement_costs",movement_b_);
cfg.add_child("defense",defense_b_);
cfg.add_child("resistance",resistance_b_);
cfg.add_child("abilities",abilities_b_);
cfg["x"] = x;
cfg["y"] = y;
std::map<std::string,unit_type>::const_iterator uti = unit_type_data::types().find(type_id());
@ -1411,14 +1376,14 @@ void unit::write(config& cfg) const
hp << hit_points_;
cfg["hitpoints"] = hp.str();
std::stringstream hpm;
hpm << max_hit_points_b_;
hpm << max_hit_points_;
cfg["max_hitpoints"] = hpm.str();
std::stringstream xp;
xp << experience_;
cfg["experience"] = xp.str();
std::stringstream xpm;
xpm << max_experience_b_;
xpm << max_experience_;
cfg["max_experience"] = xpm.str();
std::stringstream sd;
@ -1482,7 +1447,7 @@ void unit::write(config& cfg) const
cfg["goto_y"] = lexical_cast_default<std::string>(goto_.y+1);
cfg["moves"] = lexical_cast_default<std::string>(movement_);
cfg["max_moves"] = lexical_cast_default<std::string>(max_movement_b_);
cfg["max_moves"] = lexical_cast_default<std::string>(max_movement_);
cfg["resting"] = resting_ ? "yes" : "no";
@ -1515,7 +1480,7 @@ void unit::write(config& cfg) const
cfg["max_attacks"] = lexical_cast_default<std::string>(max_attacks_);
cfg["zoc"] = emit_zoc_ ? "yes" : "no";
cfg.clear_children("attack");
for(std::vector<attack_type>::const_iterator i = attacks_b_.begin(); i != attacks_b_.end(); ++i) {
for(std::vector<attack_type>::const_iterator i = attacks_.begin(); i != attacks_.end(); ++i) {
cfg.add_child("attack",i->get_cfg());
}
cfg["value"] = lexical_cast_default<std::string>(unit_value_);
@ -2211,46 +2176,44 @@ std::vector<std::pair<std::string,std::string> > unit::amla_icons() const
void unit::reset_modifications()
{
max_hit_points_ = max_hit_points_b_;
max_experience_ = max_experience_b_;
max_movement_ = max_movement_b_;
attacks_ = attacks_b_;
cfg_.clear_children("movement_costs");
cfg_.clear_children("defense");
cfg_.clear_children("resistance");
cfg_.clear_children("abilities");
cfg_.add_child("movement_costs",movement_b_);
cfg_.add_child("defense",defense_b_);
cfg_.add_child("resistance",resistance_b_);
cfg_.add_child("abilities",abilities_b_);
}
void unit::backup_state()
{
max_hit_points_b_ = max_hit_points_;
max_experience_b_ = max_experience_;
max_movement_b_ = max_movement_;
attacks_b_ = attacks_;
if(cfg_.child("movement_costs")) {
movement_b_ = *cfg_.child("movement_costs");
} else {
movement_b_ = config();
}
if(cfg_.child("defense")) {
defense_b_ = *cfg_.child("defense");
} else {
defense_b_ = config();
}
if(cfg_.child("resistance")) {
resistance_b_ = *cfg_.child("resistance");
} else {
resistance_b_ = config();
}
if(cfg_.child("abilities")) {
abilities_b_ = *cfg_.child("abilities");
} else {
abilities_b_ = config();
const std::string mod_childs[] = {"attacks","movement_costs","defense","resistance","abilities"};
const unit_type *t = type();
if(t == NULL) {
return;
}
// Reset the scalar values first
traits_description_ = "";
is_fearless_ = false;
is_healthy_ = false;
max_hit_points_ = t->hitpoints();
max_experience_ = t->experience_needed();
max_movement_ = t->movement();
attacks_ = t->attacks();
// Clear modification-related caches
modification_descriptions_.clear();
movement_costs_.clear();
defense_mods_.clear();
// Clear modified configs
foreach(const std::string& tag, mod_childs) {
cfg_.clear_children(tag);
}
// Restore unmodified configs
if(t->movement_type().get_parent()) {
//before merging the base movementtype, first get the parent movetype
//(...this doesn't look right but it was copied from advance_to()...)
cfg_.merge_with(t->movement_type().get_parent()->get_cfg());
}
config to_merge;
foreach(const std::string& tag, mod_childs) {
foreach(config* child, t->cfg_.get_children(tag)) {
to_merge.add_child(tag, *child);
}
}
cfg_.merge_with(to_merge);
}
config::child_list unit::get_modification_advances() const
@ -2356,16 +2319,7 @@ void unit::add_modification(const std::string& type, const config& mod, bool no_
// for the first time.
if(apply_to == "variation" && no_add == false) {
variation_ = (**i.first)["name"];
const unit_type_data::unit_type_map::const_iterator var = unit_type_data::types().find(type_id());
if(var == unit_type_data::types().end()) {
std::string error_message = _("Unknown unit type '$type|'");
utils::string_map symbols;
symbols["type"] = type_id();
error_message = utils::interpolate_variables_into_string(error_message, &symbols);
LOG_STREAM(err, engine) << "unit of type " << type_id() << " not found!\n";
throw game::game_error(error_message);
}
advance_to(&var->second.get_variation(variation_));
advance_to(this->type());
} else if(apply_to == "profile") {
const std::string& portrait = (**i.first)["portrait"];
const std::string& description = (**i.first)["description"];
@ -2711,14 +2665,7 @@ const unit_animation* unit::choose_animation(const game_display& disp, const gam
void unit::apply_modifications()
{
log_scope("apply mods");
reset_modifications();
modification_descriptions_.clear();
traits_description_ = "";
std::vector< t_string > traits;
is_fearless_ = false;
is_healthy_ = false;
config::child_list const &mods = modifications_.get_children("trait");
for(config::child_list::const_iterator j = mods.begin(), j_end = mods.end(); j != j_end; ++j) {
is_fearless_ = is_fearless_ || (**j)["id"] == "fearless";

View File

@ -168,8 +168,8 @@ public:
void remove_overlay(const std::string& overlay) { overlays_.erase(std::remove(overlays_.begin(),overlays_.end(),overlay),overlays_.end()); }
const std::vector<std::string>& overlays() const { return overlays_; }
/**
* Initialize this unit from a cfg object.
/**
* Initialize this unit from a cfg object.
*
* @param cfg Configuration object from which to read the unit.
* @param use_traits ??
@ -188,7 +188,7 @@ public:
/** A SDL surface, ready for display for place where we need a still-image of the unit. */
const surface still_image(bool scaled = false) const;
/**
/**
* draw a unit, fake is used for temporary unit not in unit_map (so we can
* skip functions assuming that)
*/
@ -251,7 +251,7 @@ public:
void set_interrupted_move(const gamemap::location& interrupted_move) { interrupted_move_ = interrupted_move; }
/** States for animation. */
enum STATE {
enum STATE {
STATE_STANDING, /** anim must fit in a hex */
STATE_FORGET, /** animation will be automaticaly replaced by a standing anim when finished */
STATE_ANIM}; /** normal anims */
@ -285,7 +285,6 @@ public:
void backup_state();
void apply_modifications();
void remove_temporary_modifications();
void add_trait(std::string /*trait*/);
void generate_traits(bool musthaveonly=false, game_state* state = 0);
void generate_traits_description();
std::string generate_name( simple_rng* rng = 0) const
@ -319,10 +318,6 @@ private:
void set_underlying_id();
config cfg_;
config movement_b_;
config defense_b_;
config resistance_b_;
config abilities_b_;
std::vector<std::string> advances_to_;
std::string type_;
@ -335,9 +330,9 @@ private:
std::string variation_;
int hit_points_;
int max_hit_points_, max_hit_points_b_;
int max_hit_points_;
int experience_;
int max_experience_, max_experience_b_;
int max_experience_;
int level_;
unit_type::ALIGNMENT alignment_;
std::string flag_rgb_;
@ -355,7 +350,7 @@ private:
std::vector<std::string> recruits_;
int movement_;
int max_movement_, max_movement_b_;
int max_movement_;
mutable std::map<t_translation::t_terrain, int> movement_costs_; // movement cost cache
mutable std::map<t_translation::t_terrain, int> defense_mods_; // defense modifiers cache
bool hold_position_;
@ -373,7 +368,7 @@ private:
std::string role_;
std::string ai_special_;
std::vector<attack_type> attacks_, attacks_b_;
std::vector<attack_type> attacks_;
gamemap::location::DIRECTION facing_;
t_string traits_description_;
@ -484,11 +479,11 @@ private:
* In MP games the descriptions are locally generated and might differ, so it
* should be possible to discard them. Not sure whether replays suffer the
* same problem.
*
*
* @param u the unit
*
*
* @returns the checksum for a unit
*/
*/
std::string get_checksum(const unit& u);
#endif

View File

@ -734,7 +734,7 @@ void unit_type::build_help_index(const config& cfg, const race_map& races)
race_ = &dummy_race;
}
// if num_traits is not defined, we use the num_traits from race
// if num_traits is not defined, we use the num_traits from race
num_traits_ = lexical_cast_default<unsigned int>(cfg["num_traits"], race_->num_traits());
const std::vector<std::string> genders = utils::split(cfg["gender"]);
@ -776,7 +776,8 @@ void unit_type::build_help_index(const config& cfg, const race_map& races)
const unit_type& unit_type::get_gender_unit_type(unit_race::GENDER gender) const
{
const size_t i = gender;
if(i < sizeof(gender_types_)/sizeof(*gender_types_) && gender_types_[i] != NULL) {
if(i < sizeof(gender_types_)/sizeof(*gender_types_)
&& gender_types_[i] != NULL) {
return *gender_types_[i];
}