This fixes a problem with old save games causing crashes

...by using the random function before things have been set
up. However, there are still some issues with recalled units that
previously didn't have traits getting them. I don't want this to
happen. But old save games should be playable now.
This commit is contained in:
Bruno Wolff III 2007-08-18 20:35:58 +00:00
parent a5eadd755f
commit f7b2786e97
6 changed files with 63 additions and 60 deletions

View file

@ -1553,7 +1553,7 @@ unit get_advanced_unit(const game_data& info,
if(new_type != info.unit_types.end() && un != units.end()) {
unit new_unit(un->second);
new_unit.get_experience(-new_unit.max_experience());
new_unit.advance_to(&(new_type->second), true);
new_unit.advance_to(&(new_type->second));
return new_unit;
} else {
throw game::game_error("Could not find the unit being advanced"

View file

@ -91,7 +91,7 @@ void get_player_info(const config& cfg, game_state& gamestate, std::string save_
//if this side tag describes the leader of the side
if(!utils::string_bool(cfg["no_leader"]) && cfg["controller"] != "null") {
unit new_unit(&gameinfo, &units, &map, &game_status, &teams,cfg);
unit new_unit(&gameinfo, &units, &map, &game_status, &teams, cfg, true);
//search the recall list for leader units, and if there is
//one, use it in place of the config-described unit
@ -153,7 +153,7 @@ void get_player_info(const config& cfg, game_state& gamestate, std::string save_
player->available_units.clear();
}
for(config::child_list::const_iterator su = starting_units.begin(); su != starting_units.end(); ++su) {
unit new_unit(&gameinfo, &units, &map, &game_status,&teams,**su);
unit new_unit(&gameinfo, &units, &map, &game_status,&teams,**su,true);
new_unit.set_side(side);

View file

@ -1884,7 +1884,7 @@ bool event_handler::handle_event_command(const queued_event& event_info,
wassert(units != NULL);
wassert(game_map != NULL);
wassert(status_ptr != NULL);
const unit u(game_data_ptr,units,game_map,status_ptr,teams,var);
const unit u(game_data_ptr,units,game_map,status_ptr,teams,var, false);
preferences::encountered_units().insert(u.id());
gamemap::location loc(var, game_events::get_state_of_game());

View file

@ -406,7 +406,7 @@ static player_info read_player(const game_data& data, const config* cfg)
const config::child_list& units = cfg->get_children("unit");
for(config::child_list::const_iterator i = units.begin(); i != units.end(); ++i) {
res.available_units.push_back(unit(data,**i));
res.available_units.push_back(unit(data,**i,false));
}
res.can_recruit.clear();

View file

@ -186,14 +186,14 @@ unit::unit(const unit& o):
//! Initilizes a unit from a config.
unit::unit(const game_data* gamedata, unit_map* unitmap, const gamemap* map,
const gamestatus* game_status, const std::vector<team>* teams,const config& cfg) :
const gamestatus* game_status, const std::vector<team>* teams,const config& cfg, bool use_traits) :
movement_(0), hold_position_(false),resting_(false),state_(STATE_STANDING),
facing_(gamemap::location::NORTH_EAST),flying_(false),
anim_(NULL),next_idling_(0),frame_begin_time_(0),unit_halo_(halo::NO_HALO),
unit_anim_halo_(halo::NO_HALO), draw_bars_(false),gamedata_(gamedata),
units_(unitmap), map_(map), gamestatus_(game_status),teams_(teams)
{
read(cfg);
read(cfg, use_traits);
getsHit_=0;
end_turn_ = false;
refreshing_ = false;
@ -202,14 +202,14 @@ unit::unit(const game_data* gamedata, unit_map* unitmap, const gamemap* map,
game_config::add_color_info(cfg);
}
unit::unit(const game_data& gamedata,const config& cfg) : movement_(0),
unit::unit(const game_data& gamedata,const config& cfg,bool use_traits) : movement_(0),
hold_position_(false), resting_(false), state_(STATE_STANDING),
facing_(gamemap::location::NORTH_EAST),
flying_(false),anim_(NULL),next_idling_(0),frame_begin_time_(0),
unit_halo_(halo::NO_HALO),unit_anim_halo_(halo::NO_HALO),draw_bars_(false),
gamedata_(&gamedata), units_(NULL),map_(NULL), gamestatus_(NULL)
{
read(cfg);
read(cfg,use_traits);
getsHit_=0;
end_turn_ = false;
refreshing_ = false;
@ -250,14 +250,15 @@ unit::unit(const game_data* gamedata, unit_map* unitmap, const gamemap* map,
attacks_left_ = 0;
experience_ = 0;
cfg_["upkeep"]="full";
advance_to(&t->get_gender_unit_type(gender_), use_traits);
advance_to(&t->get_gender_unit_type(gender_));
if(dummy_unit == false) validate_side(side_);
if(use_traits) {
// Units that don't have traits generated are just generic units,
// so they shouldn't get a description either.
custom_unit_description_ = generate_description();
generate_traits();
}
generate_traits(!use_traits);
apply_modifications();
if(underlying_description_.empty()){
char buf[80];
if(!custom_unit_description_.empty()){
@ -290,14 +291,15 @@ 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_), use_traits);
advance_to(&t->get_gender_unit_type(gender_));
if(dummy_unit == false) validate_side(side_);
if(use_traits) {
// Units that don't have traits generated are just generic units,
// so they shouldn't get a description either.
custom_unit_description_ = generate_description();
generate_traits();
}
generate_traits(!use_traits);
apply_modifications();
if(underlying_description_.empty()){
char buf[80];
if(!custom_unit_description_.empty()){
@ -365,10 +367,13 @@ void unit::set_game_context(const game_data* gamedata, unit_map* unitmap, const
// Note that random numbers used in config files don't work in multiplayer,
// so that leaders should be barred from all random traits until that
// is fixed. Later the restrictions will be based on play balance.
// musthavepnly is true when you don't want to generate random traits or
// you don't want to give any optional traits to a unit.
void unit::generate_traits()
void unit::generate_traits(bool musthaveonly)
{
wassert(gamedata_ != NULL);
LOG_UT << "Generating a trait for unit type " << id() << " with musthaveonly " << musthaveonly << "\n";
const game_data::unit_type_map::const_iterator type = gamedata_->unit_types.find(id());
// Calculate the unit's traits
if (type == gamedata_->unit_types.end()) {
@ -412,42 +417,45 @@ void unit::generate_traits()
}
}
// Next for leaders remove any traits that are not available to
// the "any" category.
if(can_recruit()) {
num_traits = candidate_traits.size();
m = 0;
for(size_t n = 0; n < num_traits; ++n) {
if(!(**(candidate_traits.begin()+m))["availability"].empty() ||
(**(candidate_traits.begin()+m))["availability"] != "any") {
candidate_traits.erase(candidate_traits.begin()+m);
}
else {
++m;
}
// If musthaveonly then don't generate any random/optional traits
if(!musthaveonly) {
// Next for leaders remove any traits that are not available to
// the "any" category.
if(can_recruit()) {
num_traits = candidate_traits.size();
m = 0;
for(size_t n = 0; n < num_traits; ++n) {
if(!(**(candidate_traits.begin()+m))["availability"].empty() ||
(**(candidate_traits.begin()+m))["availability"] != "any") {
candidate_traits.erase(candidate_traits.begin()+m);
}
else {
++m;
}
}
}
// Now randomly fill out to the number of traits required or until
// there aren't any more traits.
num_traits = type->second.num_traits();
for(size_t n = t; n < num_traits && candidate_traits.empty() == false; ++n) {
const size_t num = get_random()%candidate_traits.size();
traits.push_back(candidate_traits[num]);
candidate_traits.erase(candidate_traits.begin()+num);
}
}
// Now randomly fill out to the number of traits required or until
// there aren't any more traits.
num_traits = type->second.num_traits();
for(size_t n = t; n < num_traits && candidate_traits.empty() == false; ++n) {
const size_t num = get_random()%candidate_traits.size();
traits.push_back(candidate_traits[num]);
candidate_traits.erase(candidate_traits.begin()+num);
}
// Once random traits are added, don't do it again.
// Such as when restoring a saved character.
cfg_["random_traits"]="no";
}
for(std::vector<config*>::const_iterator j = traits.begin(); j != traits.end(); ++j) {
modifications_.add_child("trait",**j);
}
// Once random traits are added, don't do it again.
// Such as when restoring a saved character.
cfg_["random_traits"]="no";
}
//! Advance this unit to another type
void unit::advance_to(const unit_type* t, bool use_traits)
void unit::advance_to(const unit_type* t)
{
t = &t->get_gender_unit_type(gender_).get_variation(variation_);
reset_modifications();
@ -533,16 +541,11 @@ void unit::advance_to(const unit_type* t, bool use_traits)
}
// This will add new traits to an advancing unit, if either
// the new unit type has new "musthave" traits or the new unit type
// grants more traits than the unit currently has.
// This is meant to handle living units advancing to nonliving units.
// Note that adding random traits in multiplayer games will cause
// OOS errors. However, none of the standard advancement patterns
// add traits, only reduce them.
if (use_traits) {
generate_traits();
}
// This will add any "musthave" traits to the new unit that it doesn't
// already have. This covers the Dark Sorcerer advancing to Lich
// and gaining the "undead" trait. Random and/or optional traits are
// not added. Note that inappropiate traits are not removed.
generate_traits(true);
// Apply modifications etc, refresh the unit.
// This needs to be after type and gender are fixed,
@ -1000,7 +1003,7 @@ bool unit::internal_matches_filter(const vconfig& cfg, const gamemap::location&
//! Initialize this unit from a cfg object.
//!
//! @param cfg Configuration object from which to read the unit
void unit::read(const config& cfg)
void unit::read(const config& cfg, bool use_traits)
{
if(cfg["id"]=="" && cfg["type"]=="") {
throw game::load_game_failed("Attempt to de-serialize an empty unit");
@ -1089,7 +1092,7 @@ void unit::read(const config& cfg)
if(cfg["type"] != "" && cfg["type"] != cfg["id"] || cfg["gender"] != cfg["gender_id"]) {
std::map<std::string,unit_type>::const_iterator i = gamedata_->unit_types.find(cfg["type"]);
if(i != gamedata_->unit_types.end()) {
advance_to(&i->second.get_gender_unit_type(gender_), false);
advance_to(&i->second.get_gender_unit_type(gender_));
type_set = true;
} else {
LOG_STREAM(err, engine) << "unit of type " << cfg["type"] << " not found!\n";
@ -1213,7 +1216,7 @@ void unit::read(const config& cfg)
backup_state();
if(cfg["random_traits"].empty() ||
utils::string_bool(cfg["random_traits"])) {
generate_traits();
generate_traits(!use_traits);
}
apply_modifications();
}
@ -2459,7 +2462,7 @@ void unit::add_modification(const std::string& type, const config& mod,
if(var == gamedata_->unit_types.end()) {
throw game::game_error("Unknown unit type '" + id() + "'");
}
advance_to(&var->second.get_variation(variation_), true);
advance_to(&var->second.get_variation(variation_));
} else if(apply_to == "profile") {
const std::string& portrait = (**i.first)["portrait"];
const std::string& description = (**i.first)["description"];

View file

@ -58,8 +58,8 @@ public:
// Copy constructor
unit(const unit& u);
// Initilizes a unit from a config
unit(const game_data& gamedata, const config& cfg);
unit(const game_data* gamedata, unit_map* unitmap, const gamemap* map, const gamestatus* game_status, const std::vector<team>* teams, const config& cfg);
unit(const game_data& gamedata, const config& cfg, bool use_traits=false);
unit(const game_data* gamedata, unit_map* unitmap, const gamemap* map, const gamestatus* game_status, const std::vector<team>* teams, const config& cfg, bool use_traits=false);
// Initilizes a unit from a unit type
unit(const unit_type* t, int side, bool use_traits=false, bool dummy_unit=false, unit_race::GENDER gender=unit_race::MALE);
unit(const game_data* gamedata, unit_map* unitmap, const gamemap* map, const gamestatus* game_status, const std::vector<team>* teams, const unit_type* t, int side, bool use_traits=false, bool dummy_unit=false, unit_race::GENDER gender=unit_race::MALE);
@ -69,7 +69,7 @@ public:
void set_game_context(const game_data* gamedata, unit_map* unitmap, const gamemap* map, const gamestatus* game_status, const std::vector<team>* teams);
//! Advances this unit to another type
void advance_to(const unit_type* t, bool use_traits=false);
void advance_to(const unit_type* t);
const std::vector<std::string> advances_to() const { return advances_to_; }
//! The current type id
@ -156,7 +156,7 @@ public:
//! Initialize this unit from a cfg object.
//! @param cfg Configuration object from which to read the unit.
void read(const config& cfg);
void read(const config& cfg, bool use_traits=true);
void write(config& cfg) const;
void write(config_writer& out) const;
@ -295,7 +295,7 @@ public:
void backup_state();
void apply_modifications();
void remove_temporary_modifications();
void generate_traits();
void generate_traits(bool musthaveonly=false);
void generate_traits_description();
std::string generate_description() const { return race_->generate_name(string_gender(cfg_["gender"])); }