wesnoth/src/gamestatus.cpp
Eugen Jiresch 793bda65bf removed gamestatus member from class unit...
...(unused ctor parameter to be removed soon)
2009-06-22 10:50:30 +00:00

1108 lines
29 KiB
C++

/* $Id$ */
/*
Copyright (C) 2003 - 2009 by David White <dave@whitevine.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
or at your option any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file gamestatus.cpp
* Maintain status of a game, load&save games.
*/
#include "global.hpp"
#include "foreach.hpp"
#include "gettext.hpp"
#include "log.hpp"
#include "game_preferences.hpp"
#include "replay.hpp"
#include "statistics.hpp"
#include "unit.hpp"
#include "unit_id.hpp"
#include "wesconfig.h"
#include "wml_exception.hpp"
#include "formula_string_utils.hpp"
#include "map.hpp"
#ifndef _MSC_VER
#include <sys/time.h>
#endif
static lg::log_domain log_engine("engine");
#define ERR_NG LOG_STREAM(err, log_engine)
#define WRN_NG LOG_STREAM(warn, log_engine)
#define LOG_NG LOG_STREAM(info, log_engine)
#define DBG_NG LOG_STREAM(debug, log_engine)
#ifdef _WIN32
static void write_player(const player_info& player, config& cfg);
#endif /* _WIN32 */
game_classification::game_classification():
label(),
parent(),
version(),
campaign_type(),
campaign_define(),
campaign_xtra_defines(),
campaign(),
history(),
abbrev(),
scenario(),
next_scenario(),
completion(),
end_text(),
end_text_duration(),
difficulty("NORMAL")
{}
game_classification::game_classification(const config& cfg):
label(cfg["label"]),
parent(cfg["parent"]),
version(cfg["version"]),
campaign_type(cfg["campaign_type"]),
campaign_define(cfg["campaign_define"]),
campaign_xtra_defines(utils::split(cfg["campaign_extra_defines"])),
campaign(cfg["campaign"]),
history(cfg["history"]),
abbrev(cfg["abbrev"]),
scenario(cfg["scenario"]),
next_scenario(cfg["next_scenario"]),
completion(cfg["completion"]),
end_text(cfg["end_text"]),
end_text_duration(lexical_cast_default<unsigned int>(cfg["end_text_duration"])),
difficulty(cfg["difficulty"])
{}
game_classification::game_classification(const game_classification& gc):
label(),
parent(),
version(),
campaign_type(),
campaign_define(),
campaign_xtra_defines(),
campaign(),
history(),
abbrev(),
scenario(),
next_scenario(),
completion(),
end_text(),
end_text_duration(),
difficulty("NORMAL")
{
label = gc.label;
parent = gc.parent;
version = gc.version;
campaign_type = gc.campaign_type;
campaign_define = gc.campaign_define;
campaign_xtra_defines = gc.campaign_xtra_defines;
campaign = gc.campaign;
history = gc.history;
abbrev = gc.abbrev;
scenario = gc.scenario;
completion = gc.completion;
end_text = gc.end_text;
end_text_duration = gc.end_text_duration;
difficulty = gc.difficulty;
}
config game_classification::to_config()
{
config cfg;
cfg["label"] = label;
cfg["parent"] = parent;
cfg["version"] = game_config::version;
cfg["campaign_type"] = campaign_type;
cfg["campaign_define"] = campaign_define;
cfg["campaign_extra_defines"] = utils::join(campaign_xtra_defines);
cfg["campaign"] = campaign;
cfg["history"] = history;
cfg["abbrev"] = abbrev;
cfg["scenario"] = scenario;
cfg["next_scenario"] = next_scenario;
cfg["completion"] = completion;
cfg["end_text"] = end_text;
cfg["end_text_duration"] = str_cast<unsigned int>(end_text_duration);
cfg["difficulty"] = difficulty;
return cfg;
}
player_info::player_info() :
name(),
gold(-1) ,
gold_add(false),
available_units(),
can_recruit()
{}
player_info* game_state::get_player(const std::string& id) {
std::map< std::string, player_info >::iterator found = players.find(id);
if (found == players.end()) {
WRN_NG << "player " << id << " does not exist.\n";
return NULL;
} else
return &found->second;
}
#ifdef __UNUSED__
std::string generate_game_uuid()
{
struct timeval ts;
std::stringstream uuid;
gettimeofday(&ts, NULL);
uuid << preferences::login() << "@" << ts.tv_sec << "." << ts.tv_usec;
return uuid.str();
}
#endif
void gamestatus::add_time_area(const config& cfg)
{
areas_.push_back(area_time_of_day());
area_time_of_day &area = areas_.back();
area.id = cfg["id"];
area.xsrc = cfg["x"];
area.ysrc = cfg["y"];
std::vector<map_location> const& locs = parse_location_range(area.xsrc, area.ysrc);
std::copy(locs.begin(), locs.end(), std::inserter(area.hexes, area.hexes.end()));
time_of_day::parse_times(cfg, area.times);
}
void gamestatus::add_time_area(const std::string& id, const std::set<map_location>& locs,
const config& time_cfg)
{
areas_.push_back(area_time_of_day());
area_time_of_day& area = areas_.back();
area.id = id;
area.hexes = locs;
time_of_day::parse_times(time_cfg, area.times);
}
void gamestatus::remove_time_area(const std::string& area_id)
{
if(area_id.empty()) {
areas_.clear();
} else {
// search for all time areas that match the id.
std::vector<area_time_of_day>::iterator i = areas_.begin();
while(i != areas_.end()) {
if((*i).id == area_id) {
i = areas_.erase(i);
} else {
++i;
}
}
}
}
gamestatus::gamestatus(const config& time_cfg, int num_turns, game_state* s_o_g) :
teams(0),
times_(),
areas_(),
turn_(1),
numTurns_(num_turns),
currentTime_(0),
state_of_game_(s_o_g)
{
std::string turn_at = time_cfg["turn_at"];
std::string current_tod = time_cfg["current_tod"];
std::string random_start_time = time_cfg["random_start_time"];
if (s_o_g)
{
turn_at = utils::interpolate_variables_into_string(turn_at, *s_o_g);
current_tod = utils::interpolate_variables_into_string(current_tod, *s_o_g);
}
if(turn_at.empty() == false) {
turn_ = atoi(turn_at.c_str());
}
time_of_day::parse_times(time_cfg,times_);
set_start_ToD(const_cast<config&>(time_cfg),s_o_g);
foreach (const config &t, time_cfg.child_range("time_area")) {
this->add_time_area(t);
}
}
void gamestatus::write(config& cfg) const
{
std::stringstream buf;
buf << turn_;
cfg["turn_at"] = buf.str();
buf.str(std::string());
buf << numTurns_;
cfg["turns"] = buf.str();
buf.str(std::string());
buf << currentTime_;
cfg["current_tod"] = buf.str();
std::vector<time_of_day>::const_iterator t;
for(t = times_.begin(); t != times_.end(); ++t) {
t->write(cfg.add_child("time"));
}
for(std::vector<area_time_of_day>::const_iterator i = areas_.begin(); i != areas_.end(); ++i) {
config& area = cfg.add_child("time_area");
area["x"] = i->xsrc;
area["y"] = i->ysrc;
for(t = i->times.begin(); t != i->times.end(); ++t) {
t->write(area.add_child("time"));
}
}
}
time_of_day gamestatus::get_time_of_day_turn(int nturn) const
{
VALIDATE(times_.size(), _("No time of day has been defined."));
int time = (currentTime_ + nturn - turn())% times_.size();
if (time < 0)
{
time += times_.size();
}
return times_[time];
}
time_of_day gamestatus::get_time_of_day() const
{
VALIDATE(times_.size(), _("No time of day has been defined."));
return times_[currentTime_];
}
time_of_day gamestatus::get_previous_time_of_day() const
{
return get_time_of_day_turn(turn()-1);
}
time_of_day gamestatus::get_time_of_day(int illuminated, const map_location& loc, int n_turn) const
{
time_of_day res = get_time_of_day_turn(n_turn);
if(loc.valid()) {
for(std::vector<area_time_of_day>::const_iterator i = areas_.begin(); i != areas_.end(); ++i) {
if(i->hexes.count(loc) == 1) {
VALIDATE(i->times.size(), _("No time of day has been defined."));
res = i->times[(n_turn-1)%i->times.size()];
break;
}
}
}
if(illuminated) {
res.bonus_modified=illuminated;
res.lawful_bonus += illuminated;
}
return res;
}
time_of_day gamestatus::get_time_of_day(int illuminated, const map_location& loc) const
{
return get_time_of_day(illuminated,loc,turn());
}
bool gamestatus::set_time_of_day(int newTime)
{
// newTime can come from network so have to take run time test
if( newTime >= static_cast<int>(times_.size())
|| newTime < 0)
{
return false;
}
currentTime_ = newTime;
return true;
}
bool gamestatus::is_start_ToD(const std::string& random_start_time)
{
return !random_start_time.empty()
&& utils::string_bool(random_start_time, true);
}
void gamestatus::set_start_ToD(config &level, game_state* s_o_g)
{
if (!level["current_tod"].empty())
{
set_time_of_day(atoi(level["current_tod"].c_str()));
return;
}
std::string random_start_time = level["random_start_time"];
if (s_o_g)
{
random_start_time = utils::interpolate_variables_into_string(random_start_time, *s_o_g);
}
if (gamestatus::is_start_ToD(random_start_time))
{
std::vector<std::string> start_strings =
utils::split(random_start_time, ',', utils::STRIP_SPACES | utils::REMOVE_EMPTY);
if (utils::string_bool(random_start_time,false))
{
// We had boolean value
set_time_of_day(rand()%times_.size());
}
else
{
set_time_of_day(atoi(start_strings[rand()%start_strings.size()].c_str()) - 1);
}
}
else
{
// We have to set right ToD for oldsaves
set_time_of_day((turn() - 1) % times_.size());
}
// Setting ToD to level data
std::stringstream buf;
buf << currentTime_;
level["current_tod"] = buf.str();
}
void gamestatus::next_time_of_day()
{
VALIDATE(times_.size(), _("No time of day has been defined."));
currentTime_ = (currentTime_ + 1)%times_.size();
}
size_t gamestatus::turn() const
{
return turn_;
}
int gamestatus::number_of_turns() const
{
return numTurns_;
}
void gamestatus::modify_turns(const std::string& mod)
{
numTurns_ = std::max<int>(utils::apply_modifier(numTurns_,mod,0),-1);
}
void gamestatus::add_turns(int num)
{
numTurns_ = std::max<int>(numTurns_ + num,-1);
}
void gamestatus::set_turn(unsigned int num)
{
VALIDATE(times_.size(), _("No time of day has been defined."));
const unsigned int old_num = turn_;
// Correct ToD
currentTime_ = (num - 1) % times_.size();
if (currentTime_ < 0) {
currentTime_ += times_.size();
}
if(static_cast<int>(num) > numTurns_ && numTurns_ != -1) {
this->add_turns(numTurns_ - num);
}
turn_ = num;
LOG_NG << "changed current turn number from " << old_num << " to " << num << '\n';
}
bool gamestatus::next_turn()
{
next_time_of_day();
++turn_;
return numTurns_ == -1 || turn_ <= size_t(numTurns_);
}
static player_info read_player(const config &cfg)
{
player_info res;
res.name = cfg["name"];
res.gold = atoi(cfg["gold"].c_str());
res.gold_add = utils::string_bool(cfg["gold_add"]);
foreach (const config &u, cfg.child_range("unit")) {
res.available_units.push_back(unit(u, false));
}
res.can_recruit.clear();
const std::string &can_recruit_str = cfg["can_recruit"];
if (!can_recruit_str.empty()) {
const std::vector<std::string> can_recruit = utils::split(can_recruit_str);
std::copy(can_recruit.begin(),can_recruit.end(),std::inserter(res.can_recruit,res.can_recruit.end()));
}
return res;
}
game_state::game_state() :
players(),
scoped_variables(),
wml_menu_items(),
replay_data(),
starting_pos(),
snapshot(),
last_selected(map_location::null_location),
rng_(),
variables(),
temporaries(),
generator_setter(&recorder),
classification_()
{}
static void write_player(const player_info& player, config& cfg)
{
cfg["name"] = player.name;
char buf[50];
snprintf(buf,sizeof(buf),"%d",player.gold);
cfg["gold"] = buf;
cfg["gold_add"] = player.gold_add ? "yes" : "no";
for(std::vector<unit>::const_iterator i = player.available_units.begin();
i != player.available_units.end(); ++i) {
config new_cfg;
i->write(new_cfg);
cfg.add_child("unit",new_cfg);
DBG_NG << "added unit '" << new_cfg["id"] << "' to player '" << player.name << "'\n";
}
std::stringstream can_recruit;
std::copy(player.can_recruit.begin(),player.can_recruit.end(),std::ostream_iterator<std::string>(can_recruit,","));
std::string can_recruit_str = can_recruit.str();
// Remove the trailing comma
if(can_recruit_str.empty() == false) {
can_recruit_str.resize(can_recruit_str.size()-1);
}
cfg["can_recruit"] = can_recruit_str;
}
void write_players(game_state& gamestate, config& cfg)
{
// If there is already a player config available it means we are loading
// from a savegame. Don't do anything then, the information is already there
config::child_itors player_cfg = cfg.child_range("player");
if (player_cfg.first != player_cfg.second)
return;
for(std::map<std::string, player_info>::const_iterator i=gamestate.players.begin();
i!=gamestate.players.end(); ++i)
{
config new_cfg;
write_player(i->second, new_cfg);
new_cfg["save_id"]=i->first;
cfg.add_child("player", new_cfg);
}
}
game_state::game_state(const config& cfg, bool show_replay) :
players(),
scoped_variables(),
wml_menu_items(),
replay_data(),
starting_pos(),
snapshot(),
last_selected(map_location::null_location),
rng_(cfg),
variables(),
temporaries(),
generator_setter(&recorder),
classification_(cfg)
{
n_unit::id_manager::instance().set_save_id(lexical_cast_default<size_t>(cfg["next_underlying_unit_id"],0));
log_scope("read_game");
const config &snapshot = cfg.child("snapshot");
const config &replay_start = cfg.child("replay_start");
// We have to load era id for MP games so they can load correct era.
if (snapshot && !snapshot.empty() && !show_replay) {
this->snapshot = snapshot;
rng_.seed_random(lexical_cast_default<unsigned>(snapshot["random_calls"]));
// Midgame saves have the recall list stored in the snapshot.
load_recall_list(snapshot.child_range("player"));
} else {
assert(replay_start != NULL);
// The player information should no longer be saved to the root of the config.
// The game now looks for the info in just the snapshot or the starting position.
// Check if we find some player information in the starting position
config::const_child_itors cfg_players = replay_start.child_range("player");
if (cfg_players.first != cfg_players.second)
load_recall_list(cfg_players);
}
LOG_NG << "scenario: '" << classification_.scenario << "'\n";
LOG_NG << "next_scenario: '" << classification_.next_scenario << "'\n";
if(classification_.difficulty.empty()) {
classification_.difficulty = "NORMAL";
}
if(classification_.campaign_type.empty()) {
classification_.campaign_type = "scenario";
}
if (const config &vars = cfg.child("variables")) {
set_variables(vars);
}
set_menu_items(cfg.child_range("menu_item"));
if (const config &replay = cfg.child("replay")) {
replay_data = replay;
}
if (replay_start) {
starting_pos = replay_start;
//This is a quick hack to make replays for campaigns work again:
//The [player] information needs to be stored somewhere within the gamestate,
//because we need it later on when creating the replay savegame.
//We therefore put it inside the starting_pos, so it doesn't get lost.
//See also playcampaign::play_game, where after finishing the scenario the replay
//will be saved.
if(!starting_pos.empty()) {
foreach (const config &p, cfg.child_range("player")) {
config& cfg_player = starting_pos.add_child("player");
cfg_player.merge_with(p);
}
}
}
if (const config &stats = cfg.child("statistics")) {
statistics::fresh_stats();
statistics::read_stats(stats);
}
}
void game_state::write_snapshot(config& cfg) const
{
log_scope("write_game");
cfg["label"] = classification_.label;
cfg["history"] = classification_.history;
cfg["abbrev"] = classification_.abbrev;
cfg["version"] = game_config::version;
cfg["scenario"] = classification_.scenario;
cfg["next_scenario"] = classification_.next_scenario;
cfg["completion"] = classification_.completion;
cfg["campaign"] = classification_.campaign;
cfg["campaign_type"] = classification_.campaign_type;
cfg["difficulty"] = classification_.difficulty;
cfg["campaign_define"] = classification_.campaign_define;
cfg["campaign_extra_defines"] = utils::join(classification_.campaign_xtra_defines);
cfg["next_underlying_unit_id"] = lexical_cast<std::string>(n_unit::id_manager::instance().get_save_id());
cfg["random_seed"] = lexical_cast<std::string>(rng_.get_random_seed());
cfg["random_calls"] = lexical_cast<std::string>(rng_.get_random_calls());
cfg["end_text"] = classification_.end_text;
cfg["end_text_duration"] = str_cast<unsigned int>(classification_.end_text_duration);
cfg.add_child("variables", variables);
for(std::map<std::string, wml_menu_item *>::const_iterator j=wml_menu_items.begin();
j!=wml_menu_items.end(); ++j) {
config new_cfg;
new_cfg["id"]=j->first;
new_cfg["image"]=j->second->image;
new_cfg["description"]=j->second->description;
new_cfg["needs_select"]= (j->second->needs_select) ? "yes" : "no";
if(!j->second->show_if.empty())
new_cfg.add_child("show_if", j->second->show_if);
if(!j->second->filter_location.empty())
new_cfg.add_child("filter_location", j->second->filter_location);
if(!j->second->command.empty())
new_cfg.add_child("command", j->second->command);
cfg.add_child("menu_item", new_cfg);
}
for(std::map<std::string, player_info>::const_iterator i=players.begin();
i!=players.end(); ++i) {
config new_cfg;
::write_player(i->second, new_cfg);
new_cfg["save_id"]=i->first;
cfg.add_child("player", new_cfg);
}
}
void extract_summary_from_config(config& cfg_save, config& cfg_summary)
{
const config &cfg_snapshot = cfg_save.child("snapshot");
const config &cfg_replay_start = cfg_save.child("replay_start");
const config &cfg_replay = cfg_save.child("replay");
const bool has_replay = cfg_replay && !cfg_replay.empty();
const bool has_snapshot = cfg_snapshot && cfg_snapshot.child("side");
cfg_summary["replay"] = has_replay ? "yes" : "no";
cfg_summary["snapshot"] = has_snapshot ? "yes" : "no";
cfg_summary["label"] = cfg_save["label"];
cfg_summary["parent"] = cfg_save["parent"];
cfg_summary["campaign_type"] = cfg_save["campaign_type"];
cfg_summary["scenario"] = cfg_save["scenario"];
cfg_summary["campaign"] = cfg_save["campaign"];
cfg_summary["difficulty"] = cfg_save["difficulty"];
cfg_summary["version"] = cfg_save["version"];
cfg_summary["corrupt"] = "";
if(has_snapshot) {
cfg_summary["turn"] = cfg_snapshot["turn_at"];
if (cfg_snapshot["turns"] != "-1") {
cfg_summary["turn"] = cfg_summary["turn"].str() + "/" + cfg_snapshot["turns"].str();
}
}
// Find the first human leader so we can display their icon in the load menu.
/** @todo Ideally we should grab all leaders if there's more than 1 human player? */
std::string leader;
foreach (const config &p, cfg_save.child_range("player"))
{
if (utils::string_bool(p["canrecruit"], false)) {
leader = p["save_id"];
}
}
bool shrouded = false;
if (!leader.empty())
{
if (const config &snapshot = *(has_snapshot ? &cfg_snapshot : &cfg_replay_start))
{
foreach (const config &side, snapshot.child_range("side"))
{
if (side["controller"] != "human") {
continue;
}
if (utils::string_bool(side["shroud"])) {
shrouded = true;
}
foreach (const config &u, side.child_range("unit"))
{
if (utils::string_bool(u["canrecruit"], false)) {
leader = u["id"];
break;
}
}
}
}
}
cfg_summary["leader"] = leader;
cfg_summary["map_data"] = "";
if(!shrouded) {
if(has_snapshot) {
if (!cfg_snapshot.find_child("side", "shroud", "yes")) {
cfg_summary["map_data"] = cfg_snapshot["map_data"];
}
} else if(has_replay) {
if (!cfg_replay_start.find_child("side","shroud","yes")) {
cfg_summary["map_data"] = cfg_replay_start["map_data"];
}
}
}
}
t_string& game_state::get_variable(const std::string& key)
{
return variable_info(key, true, variable_info::TYPE_SCALAR).as_scalar();
}
const t_string& game_state::get_variable_const(const std::string& key) const
{
variable_info to_get(key, false, variable_info::TYPE_SCALAR);
if(!to_get.is_valid) return temporaries[key];
return to_get.as_scalar();
}
config& game_state::get_variable_cfg(const std::string& key)
{
return variable_info(key, true, variable_info::TYPE_CONTAINER).as_container();
}
variable_info::array_range game_state::get_variable_cfgs(const std::string& key)
{
return variable_info(key, true, variable_info::TYPE_ARRAY).as_array();
}
void game_state::set_variable(const std::string& key, const t_string& value)
{
get_variable(key) = value;
}
config& game_state::add_variable_cfg(const std::string& key, const config& value)
{
variable_info to_add(key, true, variable_info::TYPE_ARRAY);
return to_add.vars->add_child(to_add.key, value);
}
void game_state::clear_variable_cfg(const std::string& varname)
{
variable_info to_clear(varname, false, variable_info::TYPE_CONTAINER);
if(!to_clear.is_valid) return;
if(to_clear.explicit_index) {
to_clear.vars->remove_child(to_clear.key, to_clear.index);
} else {
to_clear.vars->clear_children(to_clear.key);
}
}
void game_state::clear_variable(const std::string& varname)
{
variable_info to_clear(varname, false);
if(!to_clear.is_valid) return;
if(to_clear.explicit_index) {
to_clear.vars->remove_child(to_clear.key, to_clear.index);
} else {
to_clear.vars->clear_children(to_clear.key);
to_clear.vars->remove_attribute(to_clear.key);
}
}
void game_state::load_recall_list(const config::const_child_itors &players)
{
if (players.first == players.second) return;
foreach (const config &p, players)
{
const std::string &save_id = p["save_id"];
if (save_id.empty()) {
ERR_NG << "Corrupted player entry: NULL save_id" << std::endl;
} else {
player_info player = read_player(p);
this->players.insert(std::make_pair(save_id, player));
}
}
}
static void clear_wmi(std::map<std::string, wml_menu_item*>& gs_wmi) {
std::map<std::string, wml_menu_item*>::iterator itor = gs_wmi.begin();
for(itor = gs_wmi.begin(); itor != gs_wmi.end(); ++itor) {
delete itor->second;
}
gs_wmi.clear();
}
game_state::game_state(const game_state& state) :
/* default construct everything to silence compiler warnings. */
variable_set(),
players(),
scoped_variables(),
wml_menu_items(),
replay_data(),
starting_pos(),
snapshot(),
last_selected(),
rng_(),
variables(),
temporaries(),
generator_setter(&recorder),
classification_()
{
*this = state;
}
game_state& game_state::operator=(const game_state& state)
{
if(this == &state) {
return *this;
}
rng_ = state.rng_;
players = state.players;
scoped_variables = state.scoped_variables;
classification_ = game_classification(state.classification());
clear_wmi(wml_menu_items);
std::map<std::string, wml_menu_item*>::const_iterator itor;
for (itor = state.wml_menu_items.begin(); itor != state.wml_menu_items.end(); ++itor) {
wml_menu_item*& mref = wml_menu_items[itor->first];
mref = new wml_menu_item(*(itor->second));
}
replay_data = state.replay_data;
starting_pos = state.starting_pos;
snapshot = state.snapshot;
last_selected = state.last_selected;
set_variables(state.get_variables());
return *this;
}
game_state::~game_state() {
clear_wmi(wml_menu_items);
}
void game_state::set_variables(const config& vars) {
if(!variables.empty()) {
WRN_NG << "clobbering the game_state variables\n";
WRN_NG << variables;
}
variables = vars;
}
void game_state::get_player_info(const config& cfg,
std::string save_id, std::vector<team>& teams,
const config& level, gamemap& map, unit_map& units,
gamestatus& game_status, tod_manager& tod_mng, bool snapshot)
{
player_info *player = NULL;
if(map.empty()) {
throw game::load_game_failed("Map not found");
}
if(cfg["controller"] == "human" ||
cfg["controller"] == "network" ||
cfg["controller"] == "network_ai" ||
cfg["controller"] == "human_ai") {
player = get_player(save_id);
if(player == NULL && !save_id.empty()) {
player = &players[save_id];
}
}
LOG_NG << "initializing team...\n";
std::string gold = cfg["gold"];
if(gold.empty())
gold = "100";
LOG_NG << "found gold: '" << gold << "'\n";
int ngold = lexical_cast_default<int>(gold);
/* This is the gold carry-over mechanism for subsequent campaign
scenarios. Snapshots and replays are loaded from savegames and
got their own gold information, which must not be altered here
*/
if ( (player != NULL) && (!snapshot) ) {
if(player->gold_add) {
ngold += player->gold;
} else if(player->gold >= ngold) {
ngold = player->gold;
}
player->gold = ngold;
}
LOG_NG << "set gold to '" << ngold << "'\n";
team temp_team(cfg, map, ngold);
teams.push_back(temp_team);
// Update/fix the recall list for this side,
// by setting the "side" of each unit in it
// to be the "side" of the player.
int side = lexical_cast_default<int>(cfg["side"], 1);
if(player != NULL) {
for(std::vector<unit>::iterator it = player->available_units.begin();
it != player->available_units.end(); ++it) {
it->set_side(side);
}
}
// If this team has no objectives, set its objectives
// to the level-global "objectives"
if(teams.back().objectives().empty())
teams.back().set_objectives(level["objectives"]);
// If this side tag describes the leader of the side
if(!utils::string_bool(cfg["no_leader"]) && cfg["controller"] != "null") {
unit new_unit(&units, &map, &game_status, &tod_mng, &teams, cfg, true);
// Search the recall list for leader units, and if there is one,
// use it in place of the config-described unit
if(player != NULL) {
for(std::vector<unit>::iterator it = player->available_units.begin();
it != player->available_units.end(); ++it) {
if(it->can_recruit()) {
new_unit = *it;
new_unit.set_game_context(&units, &map, &tod_mng, &teams);
player->available_units.erase(it);
break;
}
}
}
// See if the side specifies its location.
// Otherwise start it at the map-given starting position.
map_location start_pos(cfg, this);
if(cfg["x"].empty() && cfg["y"].empty()) {
start_pos = map.starting_position(side);
}
if(!start_pos.valid() || !map.on_board(start_pos)) {
throw game::load_game_failed(
"Invalid starting position (" +
lexical_cast<std::string>(start_pos.x+1) +
"," + lexical_cast<std::string>(start_pos.y+1) +
") for the leader of side " +
lexical_cast<std::string>(side) + ".");
}
utils::string_map symbols;
symbols["side"] = lexical_cast<std::string>(side);
VALIDATE(units.count(start_pos) == 0,
t_string(vgettext("Duplicate side definition for side '$side|' found.", symbols)));
units.add(map.starting_position(new_unit.side()), new_unit);
LOG_NG << "initializing side '" << cfg["side"] << "' at "
<< start_pos << '\n';
}
// If the game state specifies units that
// can be recruited for the player, add them.
if(player != NULL && player->can_recruit.empty() == false) {
teams.back().add_recruits(player->can_recruit);
}
if(player != NULL) {
player->can_recruit = teams.back().recruits();
}
// If there are additional starting units on this side
const config::child_list& starting_units = cfg.get_children("unit");
// available_units has been filled by loading the [player]-section already.
// However, we need to get the information from the snapshot,
// so we start from scratch here.
// This is rather a quick hack, originating from keeping changes
// as minimal as possible for 1.2.
// Moving [player] into [replay_start] should be the correct way to go.
if (player && snapshot){
player->available_units.clear();
}
for(config::child_list::const_iterator su = starting_units.begin(); su != starting_units.end(); ++su) {
unit new_unit(&units, &map, &game_status, &tod_mng, &teams,**su,true);
new_unit.set_side(side);
const std::string& x = (**su)["x"];
const std::string& y = (**su)["y"];
map_location loc(**su, this);
if(x.empty() && y.empty()) {
if(player) {
player->available_units.push_back(new_unit);
LOG_NG << "inserting unit on recall list for side " << new_unit.side() << "\n";
} else {
throw game::load_game_failed(
"Attempt to create a unit on the recall list for side " +
lexical_cast<std::string>(side) +
", which does not have a recall list.");
}
} else if(!loc.valid() || !map.on_board(loc)) {
throw game::load_game_failed(
"Invalid starting position (" +
lexical_cast<std::string>(loc.x+1) +
"," + lexical_cast<std::string>(loc.y+1) +
") for a unit on side " +
lexical_cast<std::string>(side) + ".");
} else {
if (units.find(loc) != units.end()) {
ERR_NG << "[unit] trying to overwrite existing unit at " << loc << "\n";
} else {
units.add(loc, new_unit);
LOG_NG << "inserting unit for side " << new_unit.side() << "\n";
}
}
}
}
void game_state::set_menu_items(const config::const_child_itors &menu_items)
{
clear_wmi(wml_menu_items);
foreach (const config &item, menu_items)
{
const std::string &id = item["id"].base_str();
wml_menu_item*& mref = wml_menu_items[id];
if(mref == NULL) {
mref = new wml_menu_item(id, &item);
} else {
WRN_NG << "duplicate menu item (" << id << ") while loading gamestate\n";
}
}
}
void player_info::debug(){
LOG_NG << "Debugging player\n";
LOG_NG << "\tName: " << name << "\n";
LOG_NG << "\tGold: " << gold << "\n";
LOG_NG << "\tAvailable units:\n";
for (std::vector<unit>::const_iterator u = available_units.begin(); u != available_units.end(); u++){
LOG_NG << "\t\t" + u->name() + "\n";
}
LOG_NG << "\tEnd available units\n";
}
wml_menu_item::wml_menu_item(const std::string& id, const config* cfg) :
name(),
image(),
description(),
needs_select(false),
show_if(),
filter_location(),
command()
{
std::stringstream temp;
temp << "menu item";
if(!id.empty()) {
temp << ' ' << id;
}
name = temp.str();
if(cfg != NULL) {
image = (*cfg)["image"];
description = (*cfg)["description"];
needs_select = utils::string_bool((*cfg)["needs_select"], false);
if (const config &c = cfg->child("show_if")) show_if = c;
if (const config &c = cfg->child("filter_location")) filter_location = c;
if (const config &c = cfg->child("command")) command = c;
}
}