mirror of
https://github.com/wesnoth/wesnoth
synced 2025-05-02 21:43:34 +00:00
1500 lines
43 KiB
C++
1500 lines
43 KiB
C++
/* $Id$ */
|
|
/*
|
|
Copyright (C) 2007
|
|
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.
|
|
*/
|
|
|
|
#include "global.hpp"
|
|
|
|
#include "dialogs.hpp"
|
|
#include "font.hpp"
|
|
#include "game_config.hpp"
|
|
#include "gettext.hpp"
|
|
#include "log.hpp"
|
|
#include "multiplayer_connect.hpp"
|
|
#include "game_preferences.hpp"
|
|
#include "statistics.hpp"
|
|
#include "show_dialog.hpp"
|
|
#include "wassert.hpp"
|
|
#include "serialization/string_utils.hpp"
|
|
#include "ai.hpp"
|
|
|
|
#define LOG_NW LOG_STREAM(info, network)
|
|
#define ERR_NW LOG_STREAM(err, network)
|
|
#define LOG_CF LOG_STREAM(info, config)
|
|
#define ERR_CF LOG_STREAM(err, config)
|
|
|
|
namespace {
|
|
const char* controller_names[] = {
|
|
"network",
|
|
"human",
|
|
"ai",
|
|
"null"
|
|
};
|
|
}
|
|
|
|
namespace mp {
|
|
|
|
connect::side::side(connect& parent, const config& cfg, int index) :
|
|
parent_(&parent),
|
|
|
|
cfg_(cfg),
|
|
|
|
index_(index),
|
|
|
|
player_number_(parent.video(), lexical_cast_default<std::string>(index+1, ""),
|
|
font::SIZE_LARGE, font::LOBBY_COLOUR),
|
|
combo_controller_(parent.disp(), parent.player_types_),
|
|
orig_controller_(parent.video(), cfg["description"], font::SIZE_SMALL),
|
|
combo_ai_algorithm_(parent.disp(), std::vector<std::string>()),
|
|
combo_faction_(parent.disp(), parent.player_factions_),
|
|
combo_leader_(parent.disp(), std::vector<std::string>()),
|
|
combo_team_(parent.disp(), parent.player_teams_),
|
|
combo_colour_(parent.disp(), parent.player_colours_),
|
|
slider_gold_(parent.video()),
|
|
slider_income_(parent.video()),
|
|
label_gold_(parent.video(), "100", font::SIZE_SMALL, font::LOBBY_COLOUR),
|
|
label_income_(parent.video(), _("Normal"), font::SIZE_SMALL, font::LOBBY_COLOUR),
|
|
allow_player_(utils::string_bool(cfg_["allow_player"], true)),
|
|
enabled_(!parent_->params_.saved_game), changed_(false),
|
|
llm_(parent.era_sides_, &parent.game_data_, enabled_ ? &combo_leader_ : NULL)
|
|
{
|
|
if(allow_player_ && enabled_) {
|
|
controller_ = parent_->default_controller_;
|
|
} else {
|
|
size_t i = CNTR_NETWORK;
|
|
//if player isn't allowed, network controller doesn't make sense
|
|
if (!allow_player_)
|
|
++i;
|
|
for(; i != CNTR_LAST; ++i) {
|
|
if(cfg_["controller"] == controller_names[i]) {
|
|
controller_ = static_cast<mp::controller>(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
slider_gold_.set_min(20);
|
|
slider_gold_.set_max(800);
|
|
slider_gold_.set_increment(25);
|
|
slider_gold_.set_value(lexical_cast_default<int>(cfg_["gold"], 100));
|
|
slider_gold_.set_measurements(80, 16);
|
|
|
|
slider_income_.set_min(-2);
|
|
slider_income_.set_max(18);
|
|
slider_income_.set_increment(1);
|
|
slider_income_.set_value(lexical_cast_default<int>(cfg_["income"], 0));
|
|
slider_income_.set_measurements(50, 16);
|
|
|
|
combo_faction_.enable(enabled_);
|
|
combo_leader_.enable(enabled_);
|
|
combo_team_.enable(enabled_);
|
|
combo_colour_.enable(enabled_);
|
|
slider_gold_.hide(!enabled_);
|
|
slider_income_.hide(!enabled_);
|
|
label_gold_.hide(!enabled_);
|
|
label_income_.hide(!enabled_);
|
|
|
|
id_ = ""; // Id is reset, and not imported from loading savegames
|
|
save_id_ = cfg_["save_id"];
|
|
faction_ = lexical_cast_default<int>(cfg_["faction"], 0);
|
|
std::vector<std::string>::const_iterator itor = std::find(parent_->team_names_.begin(), parent_->team_names_.end(), cfg_["team_name"]);
|
|
if(itor == parent_->team_names_.end()) {
|
|
wassert(!parent_->team_names_.empty());
|
|
team_ = 0;
|
|
} else {
|
|
team_ = itor - parent_->team_names_.begin();
|
|
}
|
|
colour_ = index_;
|
|
if(!cfg_["colour"].empty()) {
|
|
colour_ = game_config::color_info(cfg_["colour"]).index() - 1;
|
|
}
|
|
gold_ = lexical_cast_default<int>(cfg_["gold"], 100);
|
|
income_ = lexical_cast_default<int>(cfg_["income"], 0);
|
|
config *ai = cfg_.child("ai");
|
|
if (ai)
|
|
ai_algorithm_ = lexical_cast_default<std::string>((*ai)["ai_algorithm"], "default");
|
|
else
|
|
ai_algorithm_ = "default";
|
|
init_ai_algorithm_combo();
|
|
|
|
// "Faction name" hack
|
|
if (!enabled_) {
|
|
faction_ = 0;
|
|
std::vector<std::string> pseudo_factions;
|
|
pseudo_factions.push_back(cfg_["name"]);
|
|
combo_faction_.set_items(pseudo_factions);
|
|
combo_faction_.set_selected(0);
|
|
|
|
// Hack: if there is a unit which can recruit, use it as a
|
|
// leader. Necessary to display leader information when loading
|
|
// saves.
|
|
config::const_child_itors side_units = cfg.child_range("unit");
|
|
std::string leader_type;
|
|
for(;side_units.first != side_units.second; ++side_units.first) {
|
|
if(utils::string_bool((**side_units.first)["canrecruit"], false)) {
|
|
leader_type = (**side_units.first)["type"];
|
|
break;
|
|
}
|
|
}
|
|
std::vector<std::string> leader_name_pseudolist;
|
|
if(leader_type.empty()) {
|
|
leader_name_pseudolist.push_back("-");
|
|
} else {
|
|
game_data::unit_type_map::const_iterator leader_name =
|
|
parent_->game_data_.unit_types.find(leader_type);
|
|
if(leader_name == parent_->game_data_.unit_types.end()) {
|
|
leader_name_pseudolist.push_back("-");
|
|
} else {
|
|
leader_name_pseudolist.push_back(leader_name->second.language_name());
|
|
}
|
|
}
|
|
combo_leader_.set_items(leader_name_pseudolist);
|
|
combo_leader_.set_selected(0);
|
|
} else if(parent_->params_.use_map_settings) {
|
|
//gold, income, team, and colour are only suggestions unless explicitly locked
|
|
if(utils::string_bool(cfg_["gold_lock"], false)) {
|
|
slider_gold_.enable(false);
|
|
label_gold_.enable(false);
|
|
}
|
|
if(utils::string_bool(cfg_["income_lock"], false)) {
|
|
slider_income_.enable(false);
|
|
label_income_.enable(false);
|
|
}
|
|
if(utils::string_bool(cfg_["team_lock"], false)) {
|
|
combo_team_.enable(false);
|
|
}
|
|
if(utils::string_bool(cfg_["colour_lock"], false)) {
|
|
combo_colour_.enable(false);
|
|
}
|
|
|
|
//set the leader
|
|
leader_ = cfg_["type"];
|
|
if(!leader_.empty()) {
|
|
combo_leader_.enable(false);
|
|
llm_.set_combo(NULL);
|
|
std::vector<std::string> leader_name_pseudolist;
|
|
game_data::unit_type_map::const_iterator leader_name = parent_->game_data_.unit_types.find(leader_);
|
|
if(leader_name == parent_->game_data_.unit_types.end()) {
|
|
leader_name_pseudolist.push_back("?");
|
|
} else {
|
|
leader_name_pseudolist.push_back(leader_name->second.language_name());
|
|
}
|
|
combo_leader_.set_items(leader_name_pseudolist);
|
|
combo_leader_.set_selected(0);
|
|
}
|
|
|
|
//try to pick a faction for the sake of appearance and for filling in the blanks
|
|
if(faction_ == 0) {
|
|
std::vector<std::string> find;
|
|
std::string search_field;
|
|
if(!cfg_["faction"].empty()) {
|
|
//choose based on faction
|
|
find.push_back(cfg_["faction"]);
|
|
search_field = "id";
|
|
} else if(!cfg_["recruit"].empty()) {
|
|
//choose based on recruit
|
|
find = utils::split(cfg_["recruit"]);
|
|
search_field = "recruit";
|
|
} else if(!leader_.empty()) {
|
|
//choose based on leader
|
|
find.push_back(leader_);
|
|
search_field = "leader";
|
|
}
|
|
|
|
std::vector<std::string>::const_iterator search = find.begin();
|
|
while(search != find.end()) {
|
|
int faction_index = 0;
|
|
std::vector<config*>::const_iterator faction = parent.era_sides_.begin();
|
|
while(faction_ == 0 && faction != parent.era_sides_.end()) {
|
|
const config& side = (**faction);
|
|
std::vector<std::string> recruit;
|
|
recruit = utils::split(side[search_field]);
|
|
for(itor = recruit.begin(); itor != recruit.end(); ++itor) {
|
|
if(*itor == *search) {
|
|
faction_ = faction_index;
|
|
llm_.update_leader_list(faction_);
|
|
combo_faction_.enable(false);
|
|
}
|
|
}
|
|
++faction;
|
|
faction_index++;
|
|
}
|
|
//exit outmost loop if we've found a faction
|
|
if(!combo_faction_.enabled()) {
|
|
break;
|
|
}
|
|
++search;
|
|
}
|
|
} else {
|
|
combo_faction_.enable(false);
|
|
}
|
|
}
|
|
|
|
update_ui();
|
|
}
|
|
|
|
connect::side::side(const side& a) :
|
|
parent_(a.parent_), cfg_(a.cfg_),
|
|
index_(a.index_), id_(a.id_), save_id_(a.save_id_),
|
|
controller_(a.controller_),
|
|
faction_(a.faction_), team_(a.team_), colour_(a.colour_),
|
|
gold_(a.gold_), income_(a.income_), leader_(a.leader_), /* taken_(a.taken_), */
|
|
ai_algorithm_(a.ai_algorithm_),
|
|
player_number_(a.player_number_), combo_controller_(a.combo_controller_),
|
|
orig_controller_(a.orig_controller_),
|
|
combo_ai_algorithm_(a.combo_ai_algorithm_),
|
|
combo_faction_(a.combo_faction_), combo_leader_(a.combo_leader_),
|
|
combo_team_(a.combo_team_), combo_colour_(a.combo_colour_),
|
|
slider_gold_(a.slider_gold_), slider_income_(a.slider_income_),
|
|
label_gold_(a.label_gold_), label_income_(a.label_income_),
|
|
allow_player_(a.allow_player_), enabled_(a.enabled_),
|
|
changed_(a.changed_), llm_(a.llm_)
|
|
{
|
|
llm_.set_combo((enabled_ && leader_.empty()) ? &combo_leader_ : NULL);
|
|
}
|
|
|
|
void connect::side::add_widgets_to_scrollpane(gui::scrollpane& pane, int pos)
|
|
{
|
|
pane.add_widget(&player_number_, 0, 5 + pos);
|
|
pane.add_widget(&combo_controller_, 20, 5 + pos);
|
|
pane.add_widget(&orig_controller_, 20 + (combo_controller_.width() - orig_controller_.width()) / 2, 35 + pos + (combo_leader_.height() - orig_controller_.height()) / 2);
|
|
pane.add_widget(&combo_ai_algorithm_, 20, 35 + pos);
|
|
pane.add_widget(&combo_faction_, 135, 5 + pos);
|
|
pane.add_widget(&combo_leader_, 135, 35 + pos);
|
|
pane.add_widget(&combo_team_, 250, 5 + pos);
|
|
pane.add_widget(&combo_colour_, 365, 5 + pos);
|
|
pane.add_widget(&slider_gold_, 475, 5 + pos);
|
|
pane.add_widget(&label_gold_, 475, 35 + pos);
|
|
pane.add_widget(&slider_income_, 475 + slider_gold_.width(), 5 + pos);
|
|
pane.add_widget(&label_income_, 475 + slider_gold_.width(), 35 + pos);
|
|
}
|
|
|
|
void connect::side::process_event()
|
|
{
|
|
if(combo_controller_.changed() && combo_controller_.selected() >= 0) {
|
|
if (combo_controller_.selected() == CNTR_LAST) {
|
|
update_controller_ui();
|
|
} else if (combo_controller_.selected() < CNTR_LAST) {
|
|
// If the current side corresponds to an existing user,
|
|
// we must kick it!
|
|
|
|
// Update controller first, or else kick will reset it.
|
|
controller_ = mp::controller(combo_controller_.selected());
|
|
|
|
// Don't kick an empty player or the game creator
|
|
if(!id_.empty()) {
|
|
if (id_ != preferences::login()) {
|
|
parent_->kick_player(id_);
|
|
}
|
|
id_ = "";
|
|
}
|
|
changed_ = true;
|
|
} else {
|
|
size_t user = combo_controller_.selected() - CNTR_LAST - 1;
|
|
|
|
// If the selected user already was attributed to
|
|
// another side, find its side, and switch users.
|
|
const std::string new_id = parent_->users_[user].name;
|
|
if (new_id != id_) {
|
|
int old_side = parent_->find_player_side(new_id);
|
|
if (old_side != -1) {
|
|
if (id_.empty()) {
|
|
parent_->sides_[old_side].set_controller(controller_);
|
|
} else {
|
|
parent_->sides_[old_side].set_id(id_);
|
|
}
|
|
}
|
|
id_ = new_id;
|
|
controller_ = parent_->users_[user].controller;
|
|
changed_ = true;
|
|
}
|
|
}
|
|
update_ai_algorithm_combo();
|
|
}
|
|
|
|
if(!enabled_)
|
|
return;
|
|
|
|
if (combo_faction_.changed() && combo_faction_.selected() >= 0) {
|
|
faction_ = combo_faction_.selected();
|
|
llm_.update_leader_list(faction_);
|
|
changed_ = true;
|
|
}
|
|
if (combo_ai_algorithm_.changed() && combo_ai_algorithm_.selected() >= 0) {
|
|
ai_algorithm_ = parent_->ai_algorithms_[combo_ai_algorithm_.selected()];
|
|
changed_ = true;
|
|
}
|
|
if (combo_leader_.changed() && combo_leader_.selected() >= 0) {
|
|
changed_ = true;
|
|
}
|
|
if (combo_team_.changed() && combo_team_.selected() >= 0) {
|
|
team_ = combo_team_.selected();
|
|
changed_ = true;
|
|
}
|
|
if (combo_colour_.changed() && combo_colour_.selected() >= 0) {
|
|
colour_ = combo_colour_.selected();
|
|
changed_ = true;
|
|
}
|
|
if (slider_gold_.value() != gold_) {
|
|
gold_ = slider_gold_.value();
|
|
label_gold_.set_text(lexical_cast_default<std::string>(gold_, "0"));
|
|
changed_ = true;
|
|
}
|
|
if (slider_income_.value() != income_) {
|
|
|
|
income_ = slider_income_.value();
|
|
std::stringstream buf;
|
|
if(income_ < 0) {
|
|
buf << _("(") << income_ << _(")");
|
|
} else if(income_ > 0) {
|
|
buf << _("+") << income_;
|
|
} else {
|
|
buf << _("Normal");
|
|
}
|
|
label_income_.set_text(buf.str());
|
|
changed_ = true;
|
|
}
|
|
}
|
|
|
|
bool connect::side::changed()
|
|
{
|
|
bool res = changed_;
|
|
changed_ = false;
|
|
return res;
|
|
}
|
|
|
|
bool connect::side::available() const
|
|
{
|
|
return allow_player_ && controller_ == CNTR_NETWORK && id_.empty();
|
|
}
|
|
|
|
bool connect::side::allow_player() const
|
|
{
|
|
return allow_player_;
|
|
}
|
|
|
|
void connect::side::update_controller_ui()
|
|
{
|
|
if (id_.empty()) {
|
|
combo_controller_.set_selected(controller_);
|
|
} else {
|
|
connected_user_list::iterator player = parent_->find_player(id_);
|
|
|
|
if (player != parent_->users_.end()) {
|
|
combo_controller_.set_selected(CNTR_LAST + 1 + (player - parent_->users_.begin()));
|
|
} else {
|
|
combo_controller_.set_selected(CNTR_NETWORK);
|
|
}
|
|
}
|
|
|
|
update_ai_algorithm_combo();
|
|
}
|
|
|
|
void connect::side::hide_ai_algorithm_combo(bool invis)
|
|
{
|
|
if(!invis)
|
|
{
|
|
if(controller_ == CNTR_COMPUTER)
|
|
{
|
|
//computer selected, show AI combo
|
|
orig_controller_.hide(true);
|
|
combo_ai_algorithm_.hide(false);
|
|
} else {
|
|
//computer de-selected, hide AI combo
|
|
combo_ai_algorithm_.hide(true);
|
|
orig_controller_.hide(false);
|
|
}
|
|
} else {
|
|
combo_ai_algorithm_.hide(true);
|
|
}
|
|
}
|
|
|
|
void connect::side::init_ai_algorithm_combo()
|
|
{
|
|
wassert(parent_->ai_algorithms_.empty() == false);
|
|
|
|
int sel = 0;
|
|
std::vector<std::string> ais = parent_->ai_algorithms_;
|
|
for (unsigned int i = 0; i < ais.size(); i++) {
|
|
if (ais[i] == ai_algorithm_) {
|
|
sel = i;
|
|
}
|
|
if (ais[i] == "default") {
|
|
ais[i] = _("Default AI");
|
|
}
|
|
}
|
|
combo_ai_algorithm_.set_items(ais);
|
|
combo_ai_algorithm_.set_selected(sel);
|
|
}
|
|
|
|
void connect::side::update_ui()
|
|
{
|
|
update_controller_ui();
|
|
|
|
if (combo_faction_.selected() != faction_ && combo_faction_.selected() >= 0) {
|
|
combo_faction_.set_selected(faction_);
|
|
}
|
|
|
|
combo_team_.set_selected(team_);
|
|
combo_colour_.set_selected(colour_);
|
|
slider_gold_.set_value(gold_);
|
|
label_gold_.set_text(lexical_cast_default<std::string>(gold_, "0"));
|
|
slider_income_.set_value(income_);
|
|
std::stringstream buf;
|
|
if(income_ < 0) {
|
|
buf << _("(") << income_ << _(")");
|
|
} else if(income_ > 0) {
|
|
buf << _("+") << income_;
|
|
} else {
|
|
buf << _("Normal");
|
|
}
|
|
label_income_.set_text(buf.str());
|
|
}
|
|
|
|
config connect::side::get_config() const
|
|
{
|
|
config res(cfg_);
|
|
|
|
// If the user is allowed to change type, faction, leader etc, then
|
|
// import their new values in the config.
|
|
if(enabled_ && !parent_->era_sides_.empty()) {
|
|
// Merge the faction data to res
|
|
res.append(*(parent_->era_sides_[faction_]));
|
|
}
|
|
if(cfg_["side"].empty() || cfg_["side"] != lexical_cast<std::string>(index_ + 1)) {
|
|
res["side"] = lexical_cast<std::string>(index_ + 1);
|
|
}
|
|
res["controller"] = controller_names[controller_];
|
|
res["description"] = id_;
|
|
res["current_player"] = id_;
|
|
|
|
if (id_.empty()) {
|
|
char const *description;
|
|
switch(controller_) {
|
|
case CNTR_NETWORK:
|
|
description = N_("(Vacant slot)");
|
|
break;
|
|
case CNTR_LOCAL:
|
|
if(enabled_ && cfg_["save_id"].empty()) {
|
|
res["save_id"] = "local" + res["side"].str();
|
|
}
|
|
description = N_("Anonymous local player");
|
|
break;
|
|
case CNTR_COMPUTER:
|
|
if(enabled_ && cfg_["save_id"].empty()) {
|
|
res["save_id"] = "ai" + res["side"].str();
|
|
}
|
|
{
|
|
config *ai = res.child("ai");
|
|
if (!ai) ai = &res.add_child("ai");
|
|
#ifdef HAVE_PYTHON
|
|
if (ai_algorithm_.substr(ai_algorithm_.length() - 3) == ".py") {
|
|
(*ai)["ai_algorithm"] = "python_ai";
|
|
(*ai)["python_script"] = ai_algorithm_;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (ai_algorithm_ != "default")
|
|
(*ai)["ai_algorithm"] = ai_algorithm_;
|
|
}
|
|
}
|
|
description = N_("Computer player");
|
|
break;
|
|
case CNTR_EMPTY:
|
|
description = N_("(Empty slot)");
|
|
res["no_leader"] = "yes";
|
|
break;
|
|
default:
|
|
wassert(false);
|
|
break;
|
|
}
|
|
res["user_description"] = t_string(description, "wesnoth");
|
|
} else {
|
|
if(enabled_ && cfg_["save_id"].empty()) {
|
|
res["save_id"] = id_;
|
|
}
|
|
|
|
res["user_description"] = id_;
|
|
}
|
|
|
|
if(enabled_) {
|
|
if (leader_.empty()) {
|
|
res["type"] = llm_.get_leader();
|
|
} else {
|
|
res["type"] = leader_;
|
|
}
|
|
// res["team"] = lexical_cast<std::string>(team_);
|
|
res["team_name"] = parent_->team_names_[team_];
|
|
res["user_team_name"] = parent_->user_team_names_[team_];
|
|
res["colour"] = lexical_cast<std::string>(colour_ + 1);
|
|
res["gold"] = lexical_cast<std::string>(gold_);
|
|
res["income"] = lexical_cast<std::string>(income_);
|
|
|
|
if(!parent_->params_.use_map_settings || res["fog"].empty() || (res["fog"] != "yes" && res["fog"] != "no")) {
|
|
res["fog"] = parent_->params_.fog_game ? "yes" : "no";
|
|
}
|
|
|
|
if(!parent_->params_.use_map_settings || res["shroud"].empty() || (res["shroud"] != "yes" && res["shroud"] != "no")) {
|
|
res["shroud"] = parent_->params_.shroud_game ? "yes" : "no";
|
|
}
|
|
|
|
|
|
if(!parent_->params_.use_map_settings || res["mp_countdown"].empty() || (res["mp_countdown"] != "yes" && res["mp_countdown"] != "no")) {
|
|
res["mp_countdown"] = parent_->params_.mp_countdown ? "yes" : "no";;
|
|
}
|
|
|
|
if(!parent_->params_.use_map_settings || res["mp_countdown_init_time"].empty()) {
|
|
res["mp_countdown_init_time"] = lexical_cast<std::string>(parent_->params_.mp_countdown_init_time);
|
|
}
|
|
if(!parent_->params_.use_map_settings || res["mp_countdown_turn_bonus"].empty()) {
|
|
res["mp_countdown_turn_bonus"] = lexical_cast<std::string>(parent_->params_.mp_countdown_turn_bonus);
|
|
}
|
|
if(!parent_->params_.use_map_settings || res["mp_countdown_reservoir_time"].empty()) {
|
|
res["mp_countdown_reservoir_time"] = lexical_cast<std::string>(parent_->params_.mp_countdown_reservoir_time);
|
|
}
|
|
if(!parent_->params_.use_map_settings || res["mp_countdown_action_bonus"].empty()) {
|
|
res["mp_countdown_action_bonus"] = lexical_cast<std::string>(parent_->params_.mp_countdown_action_bonus);
|
|
}
|
|
|
|
res["share_maps"] = parent_->params_.share_maps ? "yes" : "no";
|
|
res["share_view"] = parent_->params_.share_view ? "yes" : "no";
|
|
if(!parent_->params_.use_map_settings || res["village_gold"].empty())
|
|
res["village_gold"] = lexical_cast<std::string>(parent_->params_.village_gold);
|
|
|
|
res["allow_changes"] = "yes";
|
|
} else {
|
|
res["allow_changes"] = "no";
|
|
}
|
|
|
|
if(parent_->params_.use_map_settings && enabled_) {
|
|
config trimmed = cfg_;
|
|
trimmed["side"] = "";
|
|
trimmed["controller"] = "";
|
|
trimmed["description"] = "";
|
|
trimmed["team_name"] = "";
|
|
trimmed["user_team_name"] = "";
|
|
trimmed["colour"] = "";
|
|
trimmed["gold"] = "";
|
|
trimmed["income"] = "";
|
|
trimmed["allow_changes"] = "";
|
|
if(controller_ != CNTR_COMPUTER) {
|
|
//only override names for computer controlled players
|
|
trimmed["user_description"] = "";
|
|
}
|
|
trimmed.prune();
|
|
res.merge_with(trimmed);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
void connect::side::set_controller(mp::controller controller)
|
|
{
|
|
controller_ = controller;
|
|
id_ = "";
|
|
|
|
update_ui();
|
|
}
|
|
|
|
mp::controller connect::side::get_controller() const
|
|
{
|
|
return controller_;
|
|
}
|
|
|
|
void connect::side::update_user_list()
|
|
{
|
|
bool name_present = false;
|
|
|
|
std::vector<std::string> list = parent_->player_types_;
|
|
list.push_back("----");
|
|
|
|
connected_user_list::const_iterator itor;
|
|
for (itor = parent_->users_.begin(); itor != parent_->users_.end();
|
|
++itor) {
|
|
list.push_back(itor->name);
|
|
if (itor->name == id_)
|
|
name_present = true;
|
|
}
|
|
|
|
if (name_present == false) {
|
|
id_ = "";
|
|
}
|
|
|
|
combo_controller_.set_items(list);
|
|
update_controller_ui();
|
|
}
|
|
|
|
const std::string& connect::side::get_id() const
|
|
{
|
|
return id_;
|
|
}
|
|
|
|
void connect::side::set_id(const std::string& id)
|
|
{
|
|
connected_user_list::iterator i = parent_->find_player(id);
|
|
if (i != parent_->users_.end()) {
|
|
id_ = id;
|
|
controller_ = i->controller;
|
|
}
|
|
update_ui();
|
|
}
|
|
|
|
const std::string& connect::side::get_save_id() const
|
|
{
|
|
return save_id_;
|
|
}
|
|
|
|
void connect::side::import_network_user(const config& data)
|
|
{
|
|
id_ = data["name"];
|
|
controller_ = CNTR_NETWORK;
|
|
|
|
if(enabled_ && !parent_->era_sides_.empty()) {
|
|
if(combo_faction_.enabled()) {
|
|
faction_ = lexical_cast_default<int>(data["faction"], 0);
|
|
if(faction_ > int(parent_->era_sides_.size()))
|
|
faction_ = 0;
|
|
llm_.update_leader_list(faction_);
|
|
}
|
|
if(combo_leader_.enabled()) {
|
|
llm_.set_leader(data["leader"]);
|
|
}
|
|
}
|
|
|
|
update_ui();
|
|
}
|
|
|
|
void connect::side::reset(mp::controller controller)
|
|
{
|
|
id_ = "";
|
|
controller_ = controller;
|
|
|
|
if(enabled_ && !parent_->era_sides_.empty()) {
|
|
if(combo_faction_.enabled())
|
|
faction_ = 0;
|
|
if(combo_leader_.enabled())
|
|
llm_.update_leader_list(0);
|
|
}
|
|
|
|
update_ui();
|
|
}
|
|
|
|
void connect::side::resolve_random()
|
|
{
|
|
if(!enabled_ || parent_->era_sides_.empty())
|
|
return;
|
|
|
|
if(utils::string_bool((*parent_->era_sides_[faction_])["random_faction"], false)) {
|
|
|
|
// Builds the list of sides which aren't random
|
|
std::vector<int> nonrandom_sides;
|
|
for(config::child_iterator itor = parent_->era_sides_.begin();
|
|
itor != parent_->era_sides_.end(); ++itor) {
|
|
if((**itor)["random_faction"] != "yes") {
|
|
nonrandom_sides.push_back(itor - parent_->era_sides_.begin());
|
|
}
|
|
}
|
|
|
|
if (nonrandom_sides.size() == 0) {
|
|
throw config::error(_("No non-random sides in the current era"));
|
|
}
|
|
|
|
faction_ = nonrandom_sides[rand() % nonrandom_sides.size()];
|
|
}
|
|
|
|
if (llm_.get_leader() == "random") {
|
|
// Choose a random leader type.
|
|
const config& fact = *parent_->era_sides_[faction_];
|
|
std::vector<std::string> types = utils::split(fact["random_leader"]);
|
|
if (!types.empty()) {
|
|
const int lchoice = rand() % types.size();
|
|
leader_ = types[lchoice];
|
|
} else {
|
|
// if random_leader= doesn't exists, we use leader=
|
|
types = utils::split(fact["leader"]);
|
|
if (!types.empty()) {
|
|
const int lchoice = rand() % types.size();
|
|
leader_ = types[lchoice];
|
|
} else {
|
|
utils::string_map i18n_symbols;
|
|
i18n_symbols["faction"] = fact["name"];
|
|
throw config::error(vgettext("Unable to find a leader type for faction $faction", i18n_symbols));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
connect::connect(game_display& disp, const config& game_config, const game_data& data,
|
|
chat& c, config& gamelist, const create::parameters& params,
|
|
mp::controller default_controller) :
|
|
mp::ui(disp, _("Game Lobby"), game_config, c, gamelist),
|
|
|
|
game_data_(data),
|
|
level_(),
|
|
params_(params),
|
|
|
|
team_prefix_(std::string(_("Team")) + " "),
|
|
|
|
waiting_label_(video(), "", font::SIZE_SMALL, font::LOBBY_COLOUR),
|
|
message_full_(false),
|
|
default_controller_(default_controller),
|
|
|
|
scroll_pane_(video()),
|
|
type_title_label_(video(), _("Player/Type"), font::SIZE_SMALL, font::LOBBY_COLOUR),
|
|
faction_title_label_(video(), _("Faction"), font::SIZE_SMALL, font::LOBBY_COLOUR),
|
|
team_title_label_(video(), _("Team"), font::SIZE_SMALL, font::LOBBY_COLOUR),
|
|
colour_title_label_(video(), _("Color"), font::SIZE_SMALL, font::LOBBY_COLOUR),
|
|
gold_title_label_(video(), _("Gold"), font::SIZE_SMALL, font::LOBBY_COLOUR),
|
|
income_title_label_(video(), _("Income"), font::SIZE_SMALL, font::LOBBY_COLOUR),
|
|
|
|
launch_(video(), _("I'm Ready")),
|
|
cancel_(video(), _("Cancel"))
|
|
{
|
|
// Send Initial information
|
|
config response;
|
|
config& create_game = response.add_child("create_game");
|
|
create_game["name"] = params.name;
|
|
|
|
load_game();
|
|
//The number of human-controlled sides is important to know to let the server decide,
|
|
//how many players can join this game
|
|
int human_sides = 0;
|
|
config::child_list cfg_sides = current_config()->get_children("side");
|
|
for (config::child_list::const_iterator side = cfg_sides.begin(); side != cfg_sides.end(); side++){
|
|
if ((**side)["controller"] == "human"){
|
|
human_sides++;
|
|
}
|
|
}
|
|
create_game["human_sides"] = lexical_cast<std::string>(human_sides);
|
|
network::send_data(response);
|
|
|
|
if(get_result() == QUIT)
|
|
return;
|
|
lists_init();
|
|
|
|
// Adds the current user as default user.
|
|
users_.push_back(connected_user(preferences::login(), CNTR_LOCAL, 0));
|
|
update_user_combos();
|
|
if(sides_.empty()) {
|
|
throw config::error(_("The scenario is invalid because it has no sides."));
|
|
}
|
|
|
|
//take the first available side or available side with id == login
|
|
int side_choice = -1;
|
|
for(side_list::const_iterator s = sides_.begin(); s != sides_.end(); ++s) {
|
|
if (s->allow_player()) {
|
|
if (side_choice == -1)
|
|
side_choice = s - sides_.begin();
|
|
if(s->get_save_id() == preferences::login()) {
|
|
side_choice = s - sides_.begin();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (side_choice != -1)
|
|
sides_[side_choice].set_id(preferences::login());
|
|
|
|
update_playerlist_state();
|
|
|
|
// Updates the "level_" variable, now that sides are loaded
|
|
update_level();
|
|
gamelist_updated(true);
|
|
|
|
// If we are connected, send data to the connected host
|
|
network::send_data(level_);
|
|
}
|
|
|
|
void connect::process_event()
|
|
{
|
|
bool changed = false;
|
|
|
|
for(size_t n = 0; n != sides_.size(); ++n) {
|
|
sides_[n].process_event();
|
|
if (sides_[n].changed())
|
|
changed = true;
|
|
}
|
|
|
|
if (cancel_.pressed()) {
|
|
if(network::nconnections() > 0) {
|
|
config cfg;
|
|
cfg.add_child("leave_game");
|
|
network::send_data(cfg);
|
|
}
|
|
|
|
set_result(QUIT);
|
|
return;
|
|
}
|
|
|
|
if (launch_.pressed()) {
|
|
if (!sides_available())
|
|
set_result(mp::ui::PLAY);
|
|
}
|
|
|
|
// If something has changed in the level config, send it to the
|
|
// network:
|
|
if (changed) {
|
|
update_playerlist_state();
|
|
update_and_send_diff();
|
|
}
|
|
}
|
|
|
|
const game_state& connect::get_state()
|
|
{
|
|
return state_;
|
|
}
|
|
|
|
void connect::start_game()
|
|
{
|
|
// Resolves the "random faction" and "random message"
|
|
for (side_list::iterator itor = sides_.begin(); itor != sides_.end();
|
|
++itor) {
|
|
|
|
itor->resolve_random();
|
|
}
|
|
|
|
config lock;
|
|
lock.add_child("stop_updates");
|
|
network::send_data(lock);
|
|
|
|
// Re-sends the whole level
|
|
update_and_send_diff(true);
|
|
|
|
// Build the gamestate object after updating the level
|
|
level_to_gamestate(level_, state_, params_.saved_game);
|
|
|
|
config cfg;
|
|
cfg.add_child("start_game");
|
|
network::send_data(cfg);
|
|
}
|
|
|
|
void connect::hide_children(bool hide)
|
|
{
|
|
ui::hide_children(hide);
|
|
|
|
waiting_label_.hide(hide);
|
|
// Hiding the scrollpane automatically hides its contents
|
|
scroll_pane_.hide(hide);
|
|
for (side_list::iterator itor = sides_.begin(); itor != sides_.end(); ++itor) {
|
|
itor->hide_ai_algorithm_combo(hide);
|
|
}
|
|
faction_title_label_.hide(hide);
|
|
team_title_label_.hide(hide);
|
|
colour_title_label_.hide(hide);
|
|
if (!params_.saved_game) {
|
|
gold_title_label_.hide(hide);
|
|
income_title_label_.hide(hide);
|
|
}
|
|
|
|
launch_.hide(hide);
|
|
cancel_.hide(hide);
|
|
}
|
|
|
|
void connect::gamelist_updated(bool silent)
|
|
{
|
|
update_playerlist_state(silent);
|
|
}
|
|
|
|
void connect::process_network_data(const config& data, const network::connection sock)
|
|
{
|
|
ui::process_network_data(data, sock);
|
|
|
|
if (!data["side_drop"].empty()) {
|
|
const int side_drop = lexical_cast_default<int>(data["side_drop"], 0) - 1;
|
|
if(side_drop >= 0 && side_drop < int(sides_.size())) {
|
|
connected_user_list::iterator player = find_player(sides_[side_drop].get_id());
|
|
sides_[side_drop].reset(sides_[side_drop].get_controller());
|
|
if (player != users_.end()) {
|
|
users_.erase(player);
|
|
update_user_combos();
|
|
}
|
|
update_and_send_diff();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!data["side"].empty()) {
|
|
int side_taken = lexical_cast_default<int>(data["side"], 0) - 1;
|
|
|
|
// Checks if the connecting user has a valid and unique name.
|
|
const std::string name = data["name"];
|
|
if(name.empty()) {
|
|
config response;
|
|
response.values["failed"] = "yes";
|
|
network::send_data(response,sock);
|
|
return;
|
|
}
|
|
|
|
connected_user_list::iterator player = find_player(name);
|
|
if(player != users_.end()) {
|
|
if(find_player_side(name) != -1) {
|
|
config response;
|
|
response.values["failed"] = "yes";
|
|
response["message"] = "The nick '" + name + "' is already in use.";
|
|
network::send_data(response,sock);
|
|
return;
|
|
} else {
|
|
users_.erase(player);
|
|
config observer_quit;
|
|
observer_quit.add_child("observer_quit").values["name"] = name;
|
|
network::send_data(observer_quit);
|
|
update_user_combos();
|
|
}
|
|
}
|
|
|
|
// Assigns this user to a side
|
|
if(side_taken >= 0 && side_taken < int(sides_.size())) {
|
|
if(!sides_[side_taken].available()) {
|
|
// This side is already taken. Try to reassing the player to a
|
|
// different position
|
|
side_list::const_iterator itor;
|
|
side_taken = 0;
|
|
for (itor = sides_.begin(); itor != sides_.end();
|
|
++itor, ++side_taken) {
|
|
if(itor->available())
|
|
break;
|
|
}
|
|
|
|
if(itor == sides_.end()) {
|
|
config response;
|
|
response.values["failed"] = "yes";
|
|
network::send_data(response, sock);
|
|
config kick;
|
|
kick["username"] = data["name"];
|
|
config res;
|
|
res.add_child("kick", kick);
|
|
network::send_data(res);
|
|
update_user_combos();
|
|
update_and_send_diff();
|
|
return;
|
|
}
|
|
}
|
|
|
|
LOG_CF << "client has taken a valid position\n";
|
|
|
|
// Adds the name to the list
|
|
users_.push_back(connected_user(name, CNTR_NETWORK, sock));
|
|
update_user_combos();
|
|
|
|
// sides_[side_taken].set_connection(sock);
|
|
sides_[side_taken].import_network_user(data);
|
|
update_playerlist_state();
|
|
update_and_send_diff();
|
|
|
|
LOG_NW << "sent player data\n";
|
|
|
|
} else {
|
|
ERR_CF << "tried to take illegal side: " << side_taken << '\n';
|
|
config response;
|
|
response.values["failed"] = "yes";
|
|
network::send_data(response, sock);
|
|
}
|
|
}
|
|
|
|
const config* change_faction = data.child("change_faction");
|
|
if(change_faction != NULL) {
|
|
int side_taken = find_player_side(change_faction->get_attribute("name"));
|
|
if(side_taken != -1) {
|
|
sides_[side_taken].import_network_user(*change_faction);
|
|
update_playerlist_state();
|
|
update_and_send_diff();
|
|
}
|
|
}
|
|
|
|
if(data.child("observer") != NULL) {
|
|
const t_string& observer_name = data.child("observer")->get_attribute("name");
|
|
if(!observer_name.empty()) {
|
|
connected_user_list::iterator player = find_player(observer_name);
|
|
if(player == users_.end()) {
|
|
users_.push_back(connected_user(observer_name, CNTR_NETWORK, sock));
|
|
update_user_combos();
|
|
update_playerlist_state();
|
|
update_and_send_diff();
|
|
}
|
|
}
|
|
}
|
|
if(data.child("observer_quit") != NULL) {
|
|
const t_string& observer_name = data.child("observer_quit")->get_attribute("name");
|
|
if(!observer_name.empty()) {
|
|
connected_user_list::iterator player = find_player(observer_name);
|
|
if(player != users_.end() && find_player_side(observer_name) == -1) {
|
|
users_.erase(player);
|
|
update_user_combos();
|
|
update_playerlist_state();
|
|
update_and_send_diff();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void connect::process_network_error(network::error& error)
|
|
{
|
|
//if the problem isn't related to any specific connection,
|
|
//it's a general error and we should just re-throw the error
|
|
//likewise if we are not a server, we cannot afford any connection
|
|
//to go down, so also re-throw the error
|
|
if(!error.socket || !network::is_server()) {
|
|
error.disconnect();
|
|
throw network::error(error.message);
|
|
}
|
|
|
|
bool changes = false;
|
|
|
|
//a socket has disconnected. Remove it, and resets its side
|
|
connected_user_list::iterator user;
|
|
for(user = users_.begin(); user != users_.end(); ++user) {
|
|
if(user->connection == error.socket) {
|
|
changes = true;
|
|
|
|
int i = find_player_side(user->name);
|
|
if (i != -1)
|
|
sides_[i].reset(default_controller_);
|
|
|
|
break;
|
|
}
|
|
}
|
|
if(user != users_.end()) {
|
|
users_.erase(user);
|
|
update_user_combos();
|
|
}
|
|
|
|
//now disconnect the socket
|
|
error.disconnect();
|
|
|
|
//if there have been changes to the positions taken,
|
|
//then notify other players
|
|
if(changes) {
|
|
update_and_send_diff();
|
|
update_playerlist_state();
|
|
}
|
|
}
|
|
|
|
bool connect::accept_connections()
|
|
{
|
|
if (sides_available())
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
void connect::process_network_connection(const network::connection sock)
|
|
{
|
|
ui::process_network_connection(sock);
|
|
|
|
config cfg;
|
|
cfg.add_child("join_game");
|
|
network::send_data(cfg);
|
|
|
|
network::send_data(level_, sock);
|
|
}
|
|
|
|
void connect::layout_children(const SDL_Rect& rect)
|
|
{
|
|
ui::layout_children(rect);
|
|
|
|
SDL_Rect ca = client_area();
|
|
|
|
gui::button* left_button = &launch_;
|
|
gui::button* right_button = &cancel_;
|
|
#ifdef OK_BUTTON_ON_RIGHT
|
|
std::swap(left_button,right_button);
|
|
#endif
|
|
size_t left = ca.x;
|
|
size_t right = ca.x + ca.w;
|
|
size_t top = ca.y;
|
|
size_t bottom = ca.y + ca.h;
|
|
|
|
//Buttons
|
|
right_button->set_location(right - right_button->width(),
|
|
bottom - right_button->height());
|
|
left_button->set_location(right - right_button->width() - left_button->width() - gui::ButtonHPadding,
|
|
bottom - left_button->height());
|
|
|
|
waiting_label_.set_location(left + 8, bottom-left_button->height() + 4);
|
|
type_title_label_.set_location(left+30, top+35);
|
|
faction_title_label_.set_location((left+145), top+35);
|
|
team_title_label_.set_location((left+260), top+35);
|
|
colour_title_label_.set_location((left+375), top+35);
|
|
gold_title_label_.set_location((left+493), top+35);
|
|
income_title_label_.set_location((left+560), top+35);
|
|
|
|
SDL_Rect scroll_pane_rect;
|
|
scroll_pane_rect.x = ca.x;
|
|
scroll_pane_rect.y = ca.y + 50;
|
|
scroll_pane_rect.w = ca.w;
|
|
scroll_pane_rect.h = launch_.location().y - scroll_pane_rect.y - gui::ButtonVPadding;
|
|
|
|
scroll_pane_.set_location(scroll_pane_rect);
|
|
}
|
|
|
|
void connect::lists_init()
|
|
{
|
|
//Options
|
|
player_types_.push_back(_("Network Player"));
|
|
player_types_.push_back(_("Local Player"));
|
|
player_types_.push_back(_("Computer Player"));
|
|
player_types_.push_back(_("Empty"));
|
|
|
|
for(std::vector<config*>::const_iterator faction = era_sides_.begin(); faction != era_sides_.end(); ++faction) {
|
|
player_factions_.push_back((**faction)["name"]);
|
|
}
|
|
|
|
//AI algorithms
|
|
ai_algorithms_ = get_available_ais();
|
|
|
|
//Factions
|
|
const config::child_itors sides = current_config()->child_range("side");
|
|
|
|
//Teams
|
|
config::child_iterator sd;
|
|
if(params_.use_map_settings) {
|
|
for(sd = sides.first; sd != sides.second; ++sd) {
|
|
const int side_num = sd - sides.first + 1;
|
|
t_string& team_name = (**sd)["team_name"];
|
|
t_string& user_team_name = (**sd)["user_team_name"];
|
|
|
|
if(team_name.empty())
|
|
team_name = lexical_cast<std::string>(side_num);
|
|
|
|
if(user_team_name.empty())
|
|
{
|
|
user_team_name = team_name;
|
|
}
|
|
|
|
std::vector<std::string>::const_iterator itor = std::find(team_names_.begin(), team_names_.end(), team_name);
|
|
if(itor == team_names_.end()) {
|
|
team_names_.push_back(team_name);
|
|
user_team_names_.push_back(user_team_name);
|
|
player_teams_.push_back(user_team_name.str());
|
|
}
|
|
}
|
|
} else {
|
|
std::vector<std::string> map_team_names;
|
|
for(sd = sides.first; sd != sides.second; ++sd) {
|
|
const std::string side_num = lexical_cast<std::string>(sd - sides.first + 1);
|
|
t_string& team_name = (**sd)["team_name"];
|
|
|
|
if(team_name.empty())
|
|
team_name = side_num;
|
|
|
|
std::vector<std::string>::const_iterator itor = std::find(map_team_names.begin(), map_team_names.end(), team_name);
|
|
if(itor == map_team_names.end()) {
|
|
map_team_names.push_back(team_name);
|
|
team_name = lexical_cast<std::string>(map_team_names.size());
|
|
} else {
|
|
team_name = lexical_cast<std::string>(itor - map_team_names.begin() + 1);
|
|
}
|
|
|
|
team_names_.push_back(side_num);
|
|
user_team_names_.push_back(team_prefix_ + side_num);
|
|
player_teams_.push_back(team_prefix_ + side_num);
|
|
}
|
|
}
|
|
|
|
// Colours
|
|
for(int i = 0; i < 9; ++i) {
|
|
player_colours_.push_back(get_colour_string(i));
|
|
}
|
|
|
|
// Populates "sides_" from the level configuration
|
|
sides_.reserve(sides.second - sides.first);
|
|
int index = 0;
|
|
for(sd = sides.first; sd != sides.second; ++sd, ++index) {
|
|
sides_.push_back(side(*this, **sd, index));
|
|
}
|
|
int offset=0;
|
|
// This function must be called after the sides_ vector is fully populated.
|
|
for(side_list::iterator s = sides_.begin(); s != sides_.end(); ++s) {
|
|
const int side_num = s - sides_.begin();
|
|
const int spos = 60 * (side_num-offset);
|
|
if(!utils::string_bool(s->get_config()["allow_player"], true)) {
|
|
offset++;
|
|
continue;
|
|
}
|
|
|
|
s->add_widgets_to_scrollpane(scroll_pane_, spos);
|
|
}
|
|
}
|
|
|
|
// Called by the constructor to initialize the game from a create::parameters structure.
|
|
void connect::load_game()
|
|
{
|
|
if(params_.saved_game) {
|
|
bool show_replay = false;
|
|
const std::string game = dialogs::load_game_dialog(disp(), game_config(), game_data_, &show_replay);
|
|
if(game.empty()) {
|
|
set_result(QUIT);
|
|
return;
|
|
}
|
|
|
|
std::string error_log;
|
|
{
|
|
cursor::setter cur(cursor::WAIT);
|
|
::load_game(game_data_, game, state_, &error_log);
|
|
}
|
|
if(!error_log.empty()) {
|
|
gui::show_error_message(disp(),
|
|
_("The file you have tried to load is corrupt: '") +
|
|
error_log);
|
|
set_result(QUIT);
|
|
return;
|
|
}
|
|
|
|
if(state_.campaign_type != "multiplayer") {
|
|
/* GCC-3.3 needs a temp var otherwise compilation fails */
|
|
gui::message_dialog dlg(disp(), "", _("This is not a multiplayer save"));
|
|
dlg.show();
|
|
set_result(QUIT);
|
|
return;
|
|
}
|
|
|
|
if(state_.version != game_config::version) {
|
|
// do not load if too old, if either the savegame or the current game
|
|
// has the version 'test' allow loading
|
|
if(state_.version < game_config::min_savegame_version &&
|
|
game_config::test_version.full != state_.version &&
|
|
game_config::test_version.full != game_config::version) {
|
|
|
|
/* GCC-3.3 needs a temp var otherwise compilation fails */
|
|
gui::message_dialog dlg2(disp(), "", _("This save is from a version too old to be loaded."));
|
|
dlg2.show();
|
|
set_result(QUIT);
|
|
return;
|
|
}
|
|
|
|
const int res = gui::dialog(disp(), "",
|
|
_("This save is from a different version of the game. Do you want to try to load it?"),
|
|
gui::YES_NO).show();
|
|
if(res == 1) {
|
|
set_result(QUIT);
|
|
return;
|
|
}
|
|
}
|
|
|
|
level_["savegame"] = "yes";
|
|
level_["map_data"] = state_.snapshot["map_data"];
|
|
level_["id"] = state_.snapshot["id"];
|
|
level_["name"] = state_.snapshot["name"];
|
|
level_["turn"] = state_.snapshot["turn_at"];
|
|
level_["mp_use_map_settings"] = state_.snapshot["mp_use_map_settings"];
|
|
level_["mp_village_gold"] = state_.snapshot["mp_village_gold"];
|
|
level_["mp_fog"] = state_.snapshot["mp_fog"];
|
|
level_["mp_shroud"] = state_.snapshot["mp_shroud"];
|
|
level_["mp_countdown"] = state_.snapshot["mp_countdown"];
|
|
level_["mp_countdown_init_time"] = state_.snapshot["mp_countdown_init_time"];
|
|
level_["mp_countdown_turn_bonus"] = state_.snapshot["mp_countdown_turn_bonus"];
|
|
level_["mp_countdown_action_bonus"] = state_.snapshot["mp_countdown_action_bonus"];
|
|
level_["experience_modifier"] = state_.snapshot["experience_modifier"];
|
|
level_["observer"] = state_.snapshot["observer"];
|
|
level_.add_child("snapshot") = state_.snapshot;
|
|
|
|
// Adds the replay data, and the replay start, to the level, so
|
|
// clients can receive it.
|
|
level_.add_child("replay") = state_.replay_data;
|
|
if(!state_.starting_pos.empty())
|
|
level_.add_child("replay_start") = state_.starting_pos;
|
|
level_.add_child("statistics") = statistics::write_stats();
|
|
|
|
} else {
|
|
level_["savegame"] = "no";
|
|
level_ = params_.scenario_data;
|
|
|
|
level_["hash"] = level_.hash();
|
|
level_["turns"] = lexical_cast_default<std::string>(params_.num_turns, "20");
|
|
level_["mp_countdown"] = params_.mp_countdown ? "yes" : "no";
|
|
level_["mp_countdown_init_time"] = lexical_cast_default<std::string>(params_.mp_countdown_init_time, "270");
|
|
level_["mp_countdown_turn_bonus"] = lexical_cast_default<std::string>(params_.mp_countdown_turn_bonus, "35");
|
|
level_["mp_countdown_reservoir_time"] = lexical_cast_default<std::string>(params_.mp_countdown_reservoir_time, "330");
|
|
level_["mp_countdown_action_bonus"] = lexical_cast_default<std::string>(params_.mp_countdown_action_bonus, "13");
|
|
|
|
if (params_.random_start_time)
|
|
{
|
|
if (!gamestatus::is_start_ToD(level_["random_start_time"]))
|
|
{
|
|
level_["random_start_time"] = "yes";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
level_["random_start_time"] = "no";
|
|
}
|
|
|
|
level_["scenario"] = params_.name;
|
|
level_["experience_modifier"] = lexical_cast<std::string>(params_.xp_modifier);
|
|
}
|
|
|
|
const std::string& era = params_.era;
|
|
|
|
// Initialize the list of sides available for the current era.
|
|
const config* const era_cfg = game_config().find_child("era", "id", era);
|
|
if(era_cfg == NULL) {
|
|
utils::string_map i18n_symbols;
|
|
i18n_symbols["era"] = era;
|
|
throw config::error(vgettext("Cannot find era $era", i18n_symbols));
|
|
}
|
|
era_sides_ = era_cfg->get_children("multiplayer_side");
|
|
level_.add_child("era", *era_cfg);
|
|
|
|
gold_title_label_.hide(params_.saved_game);
|
|
income_title_label_.hide(params_.saved_game);
|
|
|
|
level_["mp_village_gold"] = lexical_cast<std::string>(params_.village_gold);
|
|
level_["mp_use_map_settings"] = params_.use_map_settings ? "yes" : "no";
|
|
level_["mp_fog"] = params_.fog_game ? "yes" : "no";
|
|
level_["mp_shroud"] = params_.shroud_game ? "yes" : "no";
|
|
|
|
//this will force connecting clients to be using the same version number as us.
|
|
level_["version"] = game_config::version;
|
|
|
|
level_["observer"] = params_.allow_observers ? "yes" : "no";
|
|
|
|
if(level_["objectives"].empty()) {
|
|
level_["objectives"] = t_string(N_("Victory:\n\
|
|
@Defeat enemy leader(s)"), "wesnoth");
|
|
}
|
|
}
|
|
|
|
config* connect::current_config(){
|
|
config* cfg_level = NULL;
|
|
|
|
if (level_.child("snapshot") != NULL){
|
|
//savegame
|
|
cfg_level = level_.child("snapshot");
|
|
}
|
|
else{
|
|
//fresh game, no snapshot available
|
|
cfg_level = &level_;
|
|
}
|
|
|
|
return cfg_level;
|
|
}
|
|
|
|
void connect::update_level()
|
|
{
|
|
// Import all sides into the level
|
|
level_.clear_children("side");
|
|
for(side_list::const_iterator itor = sides_.begin(); itor != sides_.end();
|
|
++itor) {
|
|
|
|
level_.add_child("side", itor->get_config());
|
|
}
|
|
}
|
|
|
|
void connect::update_and_send_diff(bool update_time_of_day)
|
|
{
|
|
config old_level = level_;
|
|
update_level();
|
|
|
|
if (update_time_of_day)
|
|
{
|
|
// Set random start ToD
|
|
gamestatus game_status(level_,atoi(level_["turns"].c_str()),&state_);
|
|
|
|
}
|
|
|
|
config diff;
|
|
diff.add_child("scenario_diff",level_.get_diff(old_level));
|
|
network::send_data(diff);
|
|
}
|
|
|
|
bool connect::sides_available()
|
|
{
|
|
for(side_list::const_iterator itor = sides_.begin(); itor != sides_.end(); ++itor) {
|
|
if (itor->available())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void connect::update_playerlist_state(bool silent)
|
|
{
|
|
waiting_label_.set_text(sides_available() ? _("Waiting for players to join...") : "");
|
|
launch_.enable(!sides_available());
|
|
|
|
// If the "gamelist_" variable has users, use it. Else, extracts the
|
|
// user list from the actual player list.
|
|
if (gamelist().child("user") != NULL) {
|
|
ui::gamelist_updated(silent);
|
|
} else {
|
|
// Updates the player list
|
|
std::vector<std::string> playerlist;
|
|
for(connected_user_list::const_iterator itor = users_.begin(); itor != users_.end();
|
|
++itor) {
|
|
playerlist.push_back(itor->name);
|
|
}
|
|
set_user_list(playerlist, silent);
|
|
set_user_menu_items(playerlist);
|
|
}
|
|
}
|
|
|
|
connect::connected_user_list::iterator connect::find_player(const std::string& id)
|
|
{
|
|
connected_user_list::iterator itor;
|
|
for (itor = users_.begin(); itor != users_.end(); ++itor) {
|
|
if (itor->name == id)
|
|
break;
|
|
}
|
|
return itor;
|
|
}
|
|
|
|
int connect::find_player_side(const std::string& id) const
|
|
{
|
|
side_list::const_iterator itor;
|
|
for (itor = sides_.begin(); itor != sides_.end(); ++itor) {
|
|
if (itor->get_id() == id)
|
|
break;
|
|
}
|
|
|
|
if (itor == sides_.end())
|
|
return -1;
|
|
|
|
return itor - sides_.begin();
|
|
}
|
|
|
|
void connect::update_user_combos()
|
|
{
|
|
for (side_list::iterator itor = sides_.begin(); itor != sides_.end(); ++itor) {
|
|
itor->update_user_list();
|
|
}
|
|
}
|
|
|
|
void connect::kick_player(const std::string& name)
|
|
{
|
|
connected_user_list::iterator player = find_player(name);
|
|
if(player == users_.end())
|
|
return;
|
|
|
|
if(player->controller != CNTR_NETWORK)
|
|
return;
|
|
|
|
// If we are the server, kick the user ourselves; else, ask the server
|
|
// to do so.
|
|
if(network::is_server()) {
|
|
network::disconnect(player->connection);
|
|
} else {
|
|
config kick;
|
|
kick["username"] = name;
|
|
config res;
|
|
res.add_child("kick", kick);
|
|
network::send_data(res);
|
|
}
|
|
|
|
int side = find_player_side(name);
|
|
if (side != -1) {
|
|
wassert(size_t(side) < sides_.size());
|
|
sides_[side].reset(sides_[side].get_controller());
|
|
}
|
|
users_.erase(player);
|
|
update_user_combos();
|
|
}
|
|
|
|
}
|