Removed GUI1 mp dialog code

The code removed in connect_engine was used by and relied on code in multiplayer_ui.hpp, which is now removed.
This commit is contained in:
Charles Dang 2016-11-12 11:53:59 +11:00
parent 52105fdb4d
commit d5fab53976
21 changed files with 1 additions and 6838 deletions

View file

@ -868,15 +868,8 @@ set(wesnoth-main_SRC
movetype.cpp
mp_game_settings.cpp
game_initialization/mp_game_utils.cpp
game_initialization/mp_options.cpp
mp_ui_alerts.cpp
game_initialization/multiplayer.cpp
game_initialization/multiplayer_configure.cpp
game_initialization/multiplayer_connect.cpp
game_initialization/multiplayer_create.cpp
game_initialization/multiplayer_lobby.cpp
game_initialization/multiplayer_ui.cpp
game_initialization/multiplayer_wait.cpp
network_asio.cpp
pathfind/pathfind.cpp
pathfind/teleport.cpp

View file

@ -323,13 +323,6 @@ wesnoth_sources = Split("""
game_initialization/depcheck.cpp
game_initialization/flg_manager.cpp
game_initialization/mp_game_utils.cpp
game_initialization/mp_options.cpp
game_initialization/multiplayer_configure.cpp
game_initialization/multiplayer_connect.cpp
game_initialization/multiplayer_create.cpp
game_initialization/multiplayer_lobby.cpp
game_initialization/multiplayer_ui.cpp
game_initialization/multiplayer_wait.cpp
game_initialization/multiplayer.cpp
game_initialization/playcampaign.cpp
game_initialization/singleplayer.cpp

View file

@ -20,7 +20,6 @@
#include "gettext.hpp"
#include "log.hpp"
#include "map/map.hpp"
#include "game_initialization/multiplayer_ui.hpp"
#include "game_initialization/mp_game_utils.hpp"
#include "mt_rng.hpp"
#include "game_initialization/playcampaign.hpp"
@ -1417,29 +1416,6 @@ void side_engine::add_controller_option(ng::controller controller,
controller_options_.push_back(std::make_pair(controller, name));
}
std::vector<std::string> side_engine::get_colors() const
{
std::vector<std::string> res;
for (int i = 0; i < num_colors(); ++i) {
res.push_back(mp::get_color_string(get_color(i)));
}
return res;
}
std::string side_engine::get_color(int index) const
{
if(index == -1) {
index = color();
}
if(!custom_color_.empty()) {
if(index == 0) {
return custom_color_;
}
index -= 1;
}
return std::to_string(index + 1);
}
int side_engine::num_colors() const
{
return custom_color_.empty() ? gamemap::MAX_PLAYERS : gamemap::MAX_PLAYERS + 1;

View file

@ -18,8 +18,6 @@
#include "config.hpp"
#include "flg_manager.hpp"
#include "saved_game.hpp"
#include "multiplayer_ui.hpp"
#include "saved_game.hpp"
#include <set>
namespace rand_rng { class mt_rng; }
@ -222,8 +220,6 @@ public:
{ return parent_.player_teams_; }
flg_manager& flg() { return flg_; }
std::vector<std::string> get_colors() const;
std::string get_color(int index = -1) const;
int num_colors() const;
const std::string color_id() const { return color_id_; }

View file

@ -21,7 +21,6 @@
#include "game_config_manager.hpp"
#include "gettext.hpp"
#include "log.hpp"
#include "game_initialization/mp_options.hpp"
#include "savegame.hpp"
#include "units/id.hpp"
#include "wesnothd_connection_error.hpp"

View file

@ -1,598 +0,0 @@
/*
Copyright (C) 2012 - 2016 by Boldizsár Lipka <lipkab@zoho.com>
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 as published by
the Free Software Foundation; either version 2 of the License, 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 "game_initialization/mp_options.hpp"
#include "font/standard_colors.hpp"
#include "gettext.hpp"
#include "gui/auxiliary/find_widget.hpp"
#include "gui/core/window_builder.hpp"
#include "gui/dialogs/transient_message.hpp"
#include "gui/widgets/button.hpp"
#include "gui/widgets/slider.hpp"
#include "gui/widgets/text_box.hpp"
#include "gui/widgets/toggle_button.hpp"
#include "widgets/slider.hpp"
#include "widgets/textbox.hpp"
#include "utils/functional.hpp"
static lg::log_domain log_mp_create_options("mp/create/options");
#define DBG_MP LOG_STREAM(debug, log_mp_create_options)
namespace mp
{
namespace options
{
void manager::init_info(const config& cfg, const std::string& key)
{
for (const config& comp : cfg.child_range(key)) {
config entry;
entry["id"] = comp["id"];
entry["name"] = comp["name"];
if (comp.has_child("options") && comp["allow_new_game"].to_bool(true)) {
const config& options = comp.child("options");
for (const config::any_child& c : options.all_children_range()) {
entry.add_child(c.key, c.cfg);
}
}
// We need to store components even if they don't have any options in
// order to have set_xxx_by_index work properly
options_info_.add_child(key, entry);
}
}
void manager::init_widgets()
{
for (option_display* od : widgets_ordered_) {
delete od;
}
widgets_.clear();
widgets_ordered_.clear();
for (const config::any_child& comp : options_info_.all_children_range()) {
if (comp.cfg.all_children_count() == 0 || !is_active(comp.cfg["id"])) {
continue;
}
widgets_ordered_.push_back(new title_display(video_, comp.cfg["name"]));
for (const config::any_child& c : comp.cfg.all_children_range()) {
const std::string id = c.cfg["id"];
if (c.key == "slider") {
widgets_ordered_.push_back(new slider_display(video_, c.cfg));
} else if (c.key == "entry") {
widgets_ordered_.push_back(new entry_display(video_, c.cfg));
} else if (c.key == "checkbox") {
widgets_ordered_.push_back(new checkbox_display(video_, c.cfg));
} else if (c.key == "combo") {
widgets_ordered_.push_back(new combo_display(video_, c.cfg));
}
widgets_ordered_.back()->set_value(get_stored_value(id));
widgets_[id] = widgets_ordered_.back();
}
widgets_ordered_.push_back(new reset_display(video_, comp.cfg["id"], *this));
}
}
void manager::restore_defaults(const std::string &component)
{
for (const config::any_child& i : get_component_cfg(component).all_children_range()) {
if (!is_valid_option(i.key, i.cfg)) {
continue;
}
const std::string id = i.cfg["id"].str();
widgets_[id]->set_value(get_default_value(id));
}
}
bool manager::has_options() const
{
return !widgets_.empty();
}
manager::manager(const config &gamecfg, CVideo &video, gui::scrollpane *pane, const config &values)
: options_info_()
, values_(values)
, video_(video)
, pane_(pane)
, era_()
, scenario_()
, is_campaign_()
, modifications_()
, widgets_()
, widgets_ordered_()
{
DBG_MP << "Initializing the options manager" << std::endl;
init_info(gamecfg, "modification");
init_info(gamecfg, "era");
init_info(gamecfg, "multiplayer");
init_info(gamecfg, "campaign");
for (const config::any_child& i : options_info_.all_children_range())
{
for (const config::any_child& j : i.cfg.all_children_range())
{
if (is_valid_option(j.key, j.cfg)) {
config& value = get_value_cfg(j.cfg["id"]);
value["value"] = get_stored_value(j.cfg["id"]);
}
}
}
init_widgets();
}
manager::~manager()
{
for (option_display* od : widgets_ordered_)
{
delete od;
}
}
void manager::set_values(const config& c)
{
values_ = c;
}
void manager::set_era(const std::string& era)
{
era_ = era;
}
void manager::set_scenario(const std::string& scenario)
{
is_campaign_ = false;
scenario_ = scenario;
}
void manager::set_campaign(const std::string& campaign)
{
is_campaign_ = true;
scenario_ = campaign;
}
void manager::set_modifications(const std::vector<std::string>& modifications)
{
modifications_ = modifications;
}
void manager::layout_widgets(int startx, int starty, int w)
{
int ypos = starty;
int border_size = 3;
for (option_display* od : widgets_ordered_)
{
od->layout(startx, ypos, w, border_size, pane_);
ypos += border_size;
}
}
void manager::process_event()
{
for (size_t i = 0; i<widgets_ordered_.size(); ++i)
{
widgets_ordered_[i]->process_event();
}
}
void manager::hide_children(bool hide)
{
for (std::map<std::string, option_display*>::iterator i = widgets_.begin();
i != widgets_.end(); ++i)
{
i->second->hide_children(hide);
}
}
const config &manager::get_values()
{
update_values();
return values_;
}
config& manager::get_value_cfg(const std::string& id)
{
{
config& value_cfg = values_.find_child("option", "id", id);
if(value_cfg) {
return value_cfg;
}
}
config::any_child info = get_option_parent(id);
config* parent_cfg;
if (!values_.find_child(info.key, "id", info.cfg["id"])) {
parent_cfg = &values_.add_child(info.key);
(*parent_cfg)["id"] = info.cfg["id"];
} else {
parent_cfg = &values_.find_child(info.key, "id", info.cfg["id"]);
}
config& value_cfg = parent_cfg->add_child("option");
value_cfg["id"] = id;
return value_cfg;
}
const config& manager::get_value_cfg_or_empty(const std::string& id) const
{
static const config empty;
const config& cfg = values_.find_child("option", "id", id);
return cfg ? cfg : empty;
}
config::any_child manager::get_option_parent(const std::string& id) const
{
static config empty;
static const std::string empty_key = "";
static const config::any_child not_found(&empty_key, &empty);
for (const config::any_child& i : options_info_.all_children_range()) {
for (const config::any_child& j : i.cfg.all_children_range()) {
if (j.cfg["id"] == id) {
return i;
}
}
}
return not_found;
}
const config& manager::get_option_info_cfg(const std::string& id) const
{
static const config empty;
for (const config::any_child& i : options_info_.all_children_range()) {
for (const config::any_child& j : i.cfg.all_children_range()) {
if (j.cfg["id"] == id) {
return j.cfg;
}
}
}
return empty;
}
const config& manager::get_component_cfg(const std::string &id) const
{
static const config empty;
const config &m = options_info_.find_child("modification", "id", id);
if (m) {
return m;
}
const config &s = options_info_.find_child("scenario", "id", id);
if (s) {
return s;
}
const config &e = options_info_.find_child("era", "id", id);
if (e) {
return e;
}
return empty;
}
config::attribute_value manager::get_stored_value(const std::string& id) const
{
const config& valcfg = get_value_cfg_or_empty(id);
if (!valcfg["value"].empty()) {
// There's a saved value for this option
return valcfg["value"];
}
// Fall back to the option's default
return get_default_value(id);
}
config::attribute_value manager::get_default_value(const std::string& id) const
{
const config& optinfo = get_option_info_cfg(id);
return optinfo["default"];
}
void manager::extract_values(const std::string& key, const std::string& id)
{
for (const config::any_child& c : options_info_.find_child(key, "id", id).all_children_range())
{
if (!is_valid_option(c.key, c.cfg)) {
continue;
}
config& out = get_value_cfg(c.cfg["id"].str());
out["value"] = widgets_[c.cfg["id"]]->get_value();
}
}
void manager::update_values()
{
extract_values("era", era_);
if (is_campaign_) {
extract_values("campaign", scenario_);
}
else {
extract_values("multiplayer", scenario_);
}
for (const std::string& str : modifications_) {
extract_values("modification", str);
}
}
bool manager::is_valid_option(const std::string& key, const config& option)
{
return (key == "slider" || key == "entry" || key == "checkbox" || key == "combo") &&
(!option["id"].empty());
}
bool manager::is_active(const std::string &id) const
{
return (era_ == id) || (scenario_ == id) ||
(std::find(modifications_.begin(), modifications_.end(), id) != modifications_.end());
}
entry_display::entry_display(CVideo &video, const config &cfg) :
entry_(new gui::textbox(video, 150, cfg["default"])),
label_(new gui::label(video, cfg["name"]))
{
entry_->set_help_string(cfg["description"]);
}
entry_display::~entry_display()
{
delete entry_;
delete label_;
}
void entry_display::layout(int &xpos, int &ypos, int /*w*/, int border_size, gui::scrollpane *pane)
{
pane->add_widget(label_, xpos, ypos);
pane->add_widget(entry_, xpos + label_->width() + border_size, ypos);
ypos += std::max(label_->height(), entry_->height()) + border_size;
}
void entry_display::set_value(const config::attribute_value &val)
{
entry_->set_text(val);
}
config::attribute_value entry_display::get_value() const
{
config::attribute_value res;
res = entry_->text();
return res;
}
void entry_display::hide_children(bool hide)
{
label_->hide(hide);
entry_->hide(hide);
}
slider_display::slider_display(CVideo &video, const config &cfg) :
slider_(new gui::slider(video)),
label_(new gui::label(video, cfg["name"], font::SIZE_SMALL)),
last_value_(cfg["default"].to_int()),
label_text_(cfg["name"])
{
slider_->set_min(cfg["min"].to_int());
slider_->set_max(cfg["max"].to_int());
slider_->set_increment(cfg["step"].to_int());
slider_->set_value(cfg["default"].to_int());
slider_->set_help_string(cfg["description"]);
update_label();
}
slider_display::~slider_display()
{
delete slider_;
delete label_;
}
void slider_display::layout(int &xpos, int &ypos, int w, int border_size, gui::scrollpane *pane)
{
pane->add_widget(label_, xpos, ypos);
ypos += label_->height() + border_size;
pane->add_widget(slider_, xpos, ypos);
slider_->set_width(w - border_size);
ypos += slider_->height() + border_size;
}
void slider_display::set_value(const config::attribute_value &val)
{
slider_->set_value(val.to_int());
}
config::attribute_value slider_display::get_value() const
{
config::attribute_value res;
res = slider_->value();
return res;
}
void slider_display::process_event()
{
if (slider_->value() != last_value_) {
last_value_ = slider_->value();
update_label();
}
}
void slider_display::hide_children(bool hide)
{
label_->hide(hide);
slider_->hide(hide);
}
void slider_display::update_label()
{
std::stringstream ss;
ss << label_text_ << ' ' << last_value_;
label_->set_text(ss.str());
}
checkbox_display::checkbox_display(CVideo &video, const config &cfg) :
checkbox_(new gui::button(video, cfg["name"], gui::button::TYPE_CHECK))
{
checkbox_->set_check(cfg["default"].to_bool());
checkbox_->set_help_string(cfg["description"]);
}
checkbox_display::~checkbox_display()
{
delete checkbox_;
}
void checkbox_display::layout(int &xpos, int &ypos, int /*w*/, int border_size, gui::scrollpane *pane)
{
pane->add_widget(checkbox_, xpos, ypos);
ypos += checkbox_->height() + border_size;
}
void checkbox_display::set_value(const config::attribute_value &val)
{
checkbox_->set_check(val.to_bool());
}
config::attribute_value checkbox_display::get_value() const
{
config::attribute_value res;
res = checkbox_->checked();
return res;
}
void checkbox_display::hide_children(bool hide)
{
checkbox_->hide(hide);
}
title_display::title_display(CVideo &video, const std::string &label) :
title_(new gui::label(video, "`~" + label, font::SIZE_PLUS, font::LOBBY_COLOR))
{}
title_display::~title_display()
{
delete title_;
}
void title_display::layout(int &xpos, int &ypos, int /*w*/, int border_size, gui::scrollpane *pane)
{
ypos += 4*border_size;
pane->add_widget(title_, xpos, ypos);
ypos += title_->height() + 2*border_size;
}
void title_display::hide_children(bool hide)
{
title_->hide(hide);
}
combo_display::combo_display(CVideo &video, const config &cfg) :
label_(new gui::label(video, cfg["name"])),
combo_(new gui::combo(video, std::vector<std::string>())),
values_()
{
std::vector<std::string> items;
for (const config& item : cfg.child_range("item")) {
items.push_back(item["name"]);
values_.push_back(item["value"]);
}
combo_->set_items(items);
combo_->set_help_string(cfg["description"]);
set_value(cfg["default"]);
}
combo_display::~combo_display()
{
delete label_;
delete combo_;
}
void combo_display::layout(int &xpos, int &ypos, int /*w*/, int border_size, gui::scrollpane *pane)
{
pane->add_widget(label_, xpos, ypos);
pane->add_widget(combo_, xpos + label_->width() + border_size, ypos);
ypos += std::max(label_->height(), combo_->height()) + border_size;
}
void combo_display::set_value(const config::attribute_value &val)
{
const std::string value = val;
for (size_t i = 0; i<values_.size(); i++) {
if (value == values_[i]) {
combo_->set_selected(i);
break;
}
}
}
config::attribute_value combo_display::get_value() const
{
config::attribute_value res;
res = values_[combo_->selected()];
return res;
}
void combo_display::hide_children(bool hide)
{
label_->hide(hide);
combo_->hide(hide);
}
reset_display::reset_display(CVideo &video, const std::string &comp, manager &m)
: manager_(m)
, component_(comp)
, button_(new gui::button(video, _("Defaults")))
{}
reset_display::~reset_display()
{
delete button_;
}
void reset_display::layout(int &/*xpos*/, int &ypos, int w, int border_size, gui::scrollpane *pane)
{
pane->add_widget(button_, w-border_size-button_->width(), ypos);
ypos += button_->height() + border_size;
}
void reset_display::hide_children(bool hide)
{
button_->hide(hide);
}
void reset_display::process_event()
{
if (button_->pressed()) {
manager_.restore_defaults(component_);
}
}
} // namespace options
} // namespace mp

View file

@ -1,358 +0,0 @@
/*
Copyright (C) 2012 - 2016 by Boldizsár Lipka <lipkab@zoho.com>
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 as published by
the Free Software Foundation; either version 2 of the License, 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.
*/
#ifndef MP_OPTIONS_HPP_INCLUDED
#define MP_OPTIONS_HPP_INCLUDED
#include <string>
#include "config.hpp"
#include "gui/widgets/widget.hpp"
#include "gui/widgets/window.hpp"
#include "widgets/scrollpane.hpp"
#include "widgets/label.hpp"
#include "widgets/button.hpp"
#include "widgets/textbox.hpp"
#include "widgets/slider.hpp"
#include "widgets/combo.hpp"
namespace mp
{
namespace options
{
class option_display
{
public:
virtual ~option_display() {}
virtual void layout(int& xpos, int& ypos, int w, int border_size, gui::scrollpane* pane) = 0;
virtual void set_value(const config::attribute_value& val) = 0;
virtual config::attribute_value get_value() const = 0;
virtual void process_event() {}
virtual void hide_children(bool hide) = 0;
};
class entry_display : public option_display
{
public:
entry_display(CVideo& video, const config& cfg);
~entry_display();
void layout(int &xpos, int &ypos, int w, int border_size, gui::scrollpane *pane);
void set_value(const config::attribute_value &val);
config::attribute_value get_value() const;
virtual void hide_children(bool hide);
private:
gui::textbox* entry_;
gui::label* label_;
};
class slider_display : public option_display
{
public:
slider_display(CVideo& video, const config& cfg);
~slider_display();
void layout(int &xpos, int &ypos, int w, int border_size, gui::scrollpane *pane);
void set_value(const config::attribute_value &val);
config::attribute_value get_value() const;
void process_event();
virtual void hide_children(bool hide);
private:
void update_label();
gui::slider* slider_;
gui::label* label_;
int last_value_;
const std::string label_text_;
};
class checkbox_display : public option_display
{
public:
checkbox_display(CVideo& video, const config& cfg);
~checkbox_display();
void layout(int &xpos, int &ypos, int w, int border_size, gui::scrollpane *pane);
void set_value(const config::attribute_value &val);
config::attribute_value get_value() const;
virtual void hide_children(bool hide);
private:
gui::button* checkbox_;
};
class combo_display : public option_display
{
public:
combo_display(CVideo& video, const config& cfg);
~combo_display();
void layout(int &xpos, int &ypos, int w, int border_size, gui::scrollpane *pane);
void set_value(const config::attribute_value &val);
config::attribute_value get_value() const;
void hide_children(bool hide);
private:
gui::label* label_;
gui::combo* combo_;
std::vector<std::string> values_;
};
class title_display : public option_display
{
public:
title_display(CVideo& video, const std::string& label);
~title_display();
void layout(int &xpos, int &ypos, int w, int border_size, gui::scrollpane *pane);
void set_value(const config::attribute_value &/*val*/) {}
config::attribute_value get_value() const { return config::attribute_value(); }
virtual void hide_children(bool hide);
private:
gui::label* title_;
};
class manager;
class reset_display : public option_display
{
public:
reset_display(CVideo& video, const std::string& comp, manager &m);
~reset_display();
void layout(int &xpos, int &ypos, int w, int border_size, gui::scrollpane *pane);
void set_value(const config::attribute_value &/*val*/) {}
config::attribute_value get_value() const { return config::attribute_value(); }
void hide_children(bool hide);
void process_event();
private:
manager &manager_;
std::string component_;
gui::button* button_;
};
class manager
{
public:
/**
* Constructor.
*
* @param gamecfg The config object holding all eras, scenarios
* and modifications.
*
* @param display The screen to display the dialog on.
*
* @param initial_value The initial values for each option.
*/
manager(const config& gamecfg, CVideo& video, gui::scrollpane* pane, const config& initial_value);
~manager();
/**
* Set the current values the options. This overrides ALL previously set
* values, even if a not all options are provided a new value for.
*
* @param values The new values for each option.
*/
void set_values(const config& values);
/**
* Sets the selected era. Whenever show_dialog is called, only
* options for the selected era will be displayed.
*
* @param id The era's id.
*/
void set_era(const std::string& id);
/**
* Sets the selected scenario. Whenever show_dialog is called, only
* options for the selected scenario will be displayed.
*
* @param id The scenario's id.
*/
void set_scenario(const std::string& id);
/**
* Sets the selected campaign. Whenever show_dialog is called, only
* options for the campaign scenario will be displayed.
*
* @param id The campaign's id.
*/
void set_campaign(const std::string& id);
/**
* Sets the activated modifications. Whenever show_dialog is called, only
* options for the activated modifications will be displayed.
*
* @param ids The ids of the modifications
*/
void set_modifications(const std::vector<std::string>& ids);
void layout_widgets(int startx, int starty, int w);
void process_event();
void hide_children(bool hide=true);
/**
* Returns the the values for each option.
*
* @return A config containing the values.
*/
const config& get_values();
void init_widgets();
void restore_defaults(const std::string &component);
bool has_options() const;
private:
/** Stores needed info about each element and their configuration options */
config options_info_;
/** Stores the selected values for each option */
config values_;
/** The screen to display the dialog on */
CVideo& video_;
/** The scrollarea to put the widgets on */
gui::scrollpane* pane_;
/** The id of the selected era */
std::string era_;
/** The id of the selected [multiplayer] or [campaign]*/
std::string scenario_;
bool is_campaign_;
/** The ids of the selected modifications */
std::vector<std::string> modifications_;
std::map<std::string, option_display*> widgets_;
std::vector<option_display*> widgets_ordered_;
/**
* Adds the necessary information about the specified component
* to options_info_.
*
* @param cfg The component's data.
* @param key The component's type.
*/
void init_info(const config& cfg, const std::string& key);
/**
* Returns the node which holds the selected value of an option. If that
* node is not yet created, the function creates it.
*
* @param id The id of the option.
*
* @return A reference to the config which the value
* for this option should be written into.
*/
config& get_value_cfg(const std::string& id);
/**
* Returns the node which holds the selected value of an option. If that
* node is not yet created, the function returns an empty config.
*
* @param id The id of the option.
*
* @return A reference to the config which the value
* for this option should be written into or
* an empty config if that doesn't exist.
*/
const config& get_value_cfg_or_empty(const std::string& id) const;
/**
* Returns the information about an option.
*
* @param id The id of the option.
*
* @return The config object which contains the
* settings of the option, or an empty config
* if the option was not found.
*/
const config& get_option_info_cfg(const std::string& id) const;
const config& get_component_cfg(const std::string& id) const;
/**
* Finds the parent node of an options.
*
* @param id The id of the option.
*
* @return A config::any_child object containing the
* key and the data of the parent node, or ""
* for the key and an empty config if the
* option was not found.
*/
config::any_child get_option_parent(const std::string& id) const;
/**
* Retrieves the saved value for a certain option, or the default, if
* there's no such.
*
* @param id The id of the option.
*
* @return The value saved in values_ for this option
* or its specified default value if a saved
* value can't be found.
*/
config::attribute_value get_stored_value(const std::string& id) const;
/**
* Retrieves the default value for a certain option.
*
* @param id The id of the option.
*
* @return The default value for this option.
*/
config::attribute_value get_default_value(const std::string& id) const;
/**
* Writes all the values for the options of a certain component from a
* specified window into values_.
*
* @param key The component's type.
* @param id The component's id.
*/
void extract_values(const std::string& key, const std::string& id);
void update_values();
/**
* Decides whether a config is a sane option node or not.
* A valid option node:
* - Must have an id field.
* - Its key must be "slider", "entry" or "checkbox"
*
* @param key The option's key.
* @param option The option's data.
*
* @return True if the option is valid, false if not.
*/
static bool is_valid_option(const std::string& key, const config& option);
bool is_active(const std::string& id) const;
};
} // namespace options
} // namespace mp
#endif

View file

@ -38,13 +38,8 @@
#include "log.hpp"
#include "generators/map_create.hpp"
#include "game_initialization/mp_game_utils.hpp"
#include "game_initialization/multiplayer_configure.hpp"
#include "game_initialization/configure_engine.hpp"
#include "game_initialization/multiplayer_connect.hpp"
#include "game_initialization/multiplayer_create.hpp"
#include "multiplayer_error_codes.hpp"
#include "game_initialization/multiplayer_wait.hpp"
#include "game_initialization/multiplayer_lobby.hpp"
#include "game_initialization/playcampaign.hpp"
#include "settings.hpp"
#include "scripting/plugins/context.hpp"

View file

@ -1,714 +0,0 @@
/*
Copyright (C) 2013 - 2016 Boldizsár Lipka <lipkab@zoho.com>
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 as published by
the Free Software Foundation; either version 2 of the License, 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 "gettext.hpp"
#include "game_preferences.hpp"
#include "construct_dialog.hpp"
#include "settings.hpp"
#include "map/map.hpp"
#include "map/exception.hpp"
#include "generators/map_create.hpp"
#include "gui/dialogs/message.hpp"
#include "gui/dialogs/multiplayer/mp_create_game_set_password.hpp"
#include "gui/dialogs/transient_message.hpp"
#include "minimap.hpp"
#include "mp_game_settings.hpp"
#include "game_initialization/multiplayer_configure.hpp"
#include "filesystem.hpp"
#include "log.hpp"
#include "saved_game.hpp"
#include "scripting/plugins/context.hpp"
#include "wml_exception.hpp"
#include "wml_separators.hpp"
#include "formula/string_utils.hpp"
#include "font/standard_colors.hpp"
#include "utils/functional.hpp"
static lg::log_domain log_config("config");
#define ERR_CF LOG_STREAM(err, log_config)
static lg::log_domain log_mp_configure("mp/configure");
#define DBG_MP LOG_STREAM(debug, log_mp_configure)
namespace mp {
configure::nolock_settings::nolock_settings(CVideo& video)
: turns_slider_(video)
, turns_label_(video, "", font::SIZE_SMALL, font::LOBBY_COLOR)
, village_gold_slider_(video)
, village_gold_label_(video, "", font::SIZE_SMALL, font::LOBBY_COLOR)
, village_support_slider_(video)
, village_support_label_(video, "", font::SIZE_SMALL, font::LOBBY_COLOR)
, xp_modifier_slider_(video)
, xp_modifier_label_(video, "", font::SIZE_SMALL, font::LOBBY_COLOR)
, generic_label_(video, "`~ " + _("Generic"), font::SIZE_PLUS, font::LOBBY_COLOR)
, use_map_settings_(video, _("Use map settings"), gui::button::TYPE_CHECK)
, random_start_time_(video, _("Random start time"), gui::button::TYPE_CHECK)
, fog_game_(video, _("Fog of war"), gui::button::TYPE_CHECK)
, shroud_game_(video, _("Shroud"), gui::button::TYPE_CHECK)
{
}
configure::configure(CVideo& video, wesnothd_connection* connection, const config &cfg, chat& c, config& gamelist, saved_game& game, bool local_players_only) :
ui(video, connection, _("Configure Game"), cfg, c, gamelist),
local_players_only_(local_players_only),
tooltip_manager_(video),
mp_countdown_init_time_(270),
mp_countdown_reservoir_time_(330),
countdown_game_(video, _("Time limit"), gui::button::TYPE_CHECK),
countdown_init_time_slider_(video),
countdown_init_time_label_(video, "", font::SIZE_SMALL, font::LOBBY_COLOR),
countdown_reservoir_time_slider_(video),
countdown_reservoir_time_label_(video, "", font::SIZE_SMALL, font::LOBBY_COLOR),
countdown_turn_bonus_label_(video, "", font::SIZE_SMALL, font::LOBBY_COLOR),
countdown_turn_bonus_slider_(video),
countdown_action_bonus_label_(video, "", font::SIZE_SMALL, font::LOBBY_COLOR),
countdown_action_bonus_slider_(video),
name_entry_label_(video, _("Name of game:"), font::SIZE_PLUS, font::LOBBY_COLOR),
observers_game_(video, _("Observers"), gui::button::TYPE_CHECK),
registered_users_only_(video, _("Registered users only"), gui::button::TYPE_CHECK),
oos_debug_(video, _("Debug OOS"), gui::button::TYPE_CHECK),
shuffle_sides_(video, _("Shuffle sides"), gui::button::TYPE_CHECK),
random_faction_mode_label_(video, _("Random factions:"), font::SIZE_SMALL, font::LOBBY_COLOR),
random_faction_mode_(video, std::vector<std::string>()),
cancel_game_(video, _("Back")),
launch_game_(video, _("OK")),
password_button_(video, _("Set Password...")),
name_entry_(video, 32),
entry_points_label_(video, _("Select an entry point:"), font::SIZE_SMALL, font::LOBBY_COLOR),
entry_points_combo_(video, std::vector<std::string>()),
options_pane_left_(video),
options_pane_right_(video),
entry_points_(),
show_entry_points_(false),
force_use_map_settings_check_(false),
state_(game),
parameters_(state_.mp_settings()),
engine_(state_),
options_manager_(cfg, video, &options_pane_right_, engine_.options_default()),
nolock_settings_(engine_.force_lock_settings() ? 0 : new nolock_settings(video))
{
// Build the list of scenarios to play
DBG_MP << "constructing multiplayer configure dialog" << std::endl;
countdown_game_.set_check(engine_.mp_countdown_default());
countdown_game_.set_help_string(_("Enables user time limit"));
countdown_init_time_slider_.set_min(30);
countdown_init_time_slider_.set_max(1500);
countdown_init_time_slider_.set_increment(30);
countdown_init_time_slider_.set_value(engine_.mp_countdown_init_time_default());
countdown_init_time_slider_.set_help_string(_("Longest time allowed for first turn (seconds)"));
countdown_reservoir_time_slider_.set_min(30);
countdown_reservoir_time_slider_.set_max(1500);
countdown_reservoir_time_slider_.set_increment(30);
countdown_reservoir_time_slider_.set_value(engine_.mp_countdown_reservoir_time_default());
countdown_reservoir_time_slider_.set_help_string(_("Longest time possible for any turn (seconds)"));
countdown_turn_bonus_slider_.set_min(10);
countdown_turn_bonus_slider_.set_max(300);
countdown_turn_bonus_slider_.set_increment(5);
countdown_turn_bonus_slider_.set_value(engine_.mp_countdown_turn_bonus_default());
countdown_turn_bonus_slider_.set_help_string(_("Time for general tasks each turn (seconds)"));
countdown_action_bonus_slider_.set_min(0);
countdown_action_bonus_slider_.set_max(30);
countdown_action_bonus_slider_.set_increment(1);
countdown_action_bonus_slider_.set_value(engine_.mp_countdown_action_bonus_default());
countdown_action_bonus_slider_.set_help_string(_("Time for each attack, recruit, and capture"));
observers_game_.set_check(engine_.allow_observers_default());
observers_game_.set_help_string(_("Allow users who are not playing to watch the game"));
observers_game_.enable(state_.classification().campaign_type != game_classification::CAMPAIGN_TYPE::SCENARIO);
registered_users_only_.set_check(engine_.registered_users_only_default());
registered_users_only_.set_help_string(_("Allow only registered users to join the game"));
registered_users_only_.enable(state_.classification().campaign_type != game_classification::CAMPAIGN_TYPE::SCENARIO);
oos_debug_.set_check(false);
oos_debug_.set_help_string(_("More checks for OOS errors but also more network traffic"));
oos_debug_.enable(true);
shuffle_sides_.set_check(engine_.shuffle_sides_default());
shuffle_sides_.set_help_string(_("Assign sides to players at random"));
random_faction_mode_label_.set_help_string(_("Allow for mirror matchups when random factions are chosen"));
std::vector<std::string> translated_modes;
for(size_t i = 0; i < mp_game_settings::RANDOM_FACTION_MODE::count; ++i) {
std::string mode_str = mp_game_settings::RANDOM_FACTION_MODE::from_int(i).to_string();
translated_modes.push_back(translation::gettext(mode_str.c_str()));
}
random_faction_mode_.set_items(translated_modes);
random_faction_mode_.set_selected(engine_.random_faction_mode().cast<int>());
random_faction_mode_.set_help_string(_("Independent: Random factions assigned independently\nNo Mirror: No two players will get the same faction\nNo Ally Mirror: No two allied players will get the same faction"));
if(nolock_settings_) {
nolock_settings_->use_map_settings_.enable(!engine_.force_lock_settings());
nolock_settings_->use_map_settings_.set_check(engine_.use_map_settings());
nolock_settings_->use_map_settings_.set_help_string(_("Use scenario specific settings"));
nolock_settings_->turns_slider_.set_min(settings::turns_min);
nolock_settings_->turns_slider_.set_max(settings::turns_max);
nolock_settings_->turns_slider_.set_increment(settings::turns_step);
nolock_settings_->turns_slider_.set_value(engine_.num_turns_default());
nolock_settings_->turns_slider_.set_help_string(_("The maximum number of turns the game can last"));
nolock_settings_->turns_slider_.enable(!engine_.use_map_settings());
nolock_settings_->village_gold_slider_.set_min(1);
nolock_settings_->village_gold_slider_.set_max(5);
nolock_settings_->village_gold_slider_.set_value(engine_.village_gold_default());
nolock_settings_->village_gold_slider_.set_help_string(_("The amount of income each village yields per turn"));
nolock_settings_->village_gold_slider_.enable(!engine_.use_map_settings());
nolock_settings_->village_support_slider_.set_min(0);
nolock_settings_->village_support_slider_.set_max(4);
nolock_settings_->village_support_slider_.set_value(engine_.village_support_default());
nolock_settings_->village_support_slider_.set_help_string(_("The number of unit levels each village can support"));
nolock_settings_->village_support_slider_.enable(!engine_.use_map_settings());
nolock_settings_->xp_modifier_slider_.set_min(30);
nolock_settings_->xp_modifier_slider_.set_max(200);
nolock_settings_->xp_modifier_slider_.set_value(engine_.xp_modifier_default());
nolock_settings_->xp_modifier_slider_.set_increment(10);
nolock_settings_->xp_modifier_slider_.set_help_string(_("The amount of experience a unit needs to advance"));
nolock_settings_->xp_modifier_slider_.enable(!engine_.use_map_settings());
nolock_settings_->random_start_time_.set_check(engine_.random_start_time_default());
nolock_settings_->random_start_time_.set_help_string(_("Randomize time of day in begin"));
nolock_settings_->random_start_time_.enable(!engine_.use_map_settings());
nolock_settings_->fog_game_.set_check(engine_.fog_game_default());
nolock_settings_->fog_game_.set_help_string(_("Enemy units cannot be seen unless they are in range of your units"));
nolock_settings_->fog_game_.enable(!engine_.use_map_settings());
nolock_settings_->shroud_game_.set_check(engine_.shroud_game_default());
nolock_settings_->shroud_game_.set_help_string(_("The map is unknown until your units explore it"));
nolock_settings_->shroud_game_.enable(!engine_.use_map_settings());
}
#if 0
// The possible vision settings
std::vector<std::string> vision_types;
vision_types.push_back(_("Share View"));
vision_types.push_back(_("Share Maps"));
vision_types.push_back(_("Share None"));
#endif
// The starting points for campaign.
if (engine_.entry_point_titles().size() > 1) {
entry_points_combo_.set_items(engine_.entry_point_titles());
entry_points_combo_.set_selected(0);
show_entry_points_ = true;
}
options_manager_.set_era(parameters_.mp_era);
if(state_.classification().campaign.empty()) {
options_manager_.set_scenario(state_.get_scenario_id());
}
else {
options_manager_.set_campaign(state_.classification().campaign);
}
options_manager_.set_modifications(parameters_.active_mods);
options_manager_.init_widgets();
name_entry_.set_text(engine_.game_name_default());
gamelist_updated();
plugins_context_.reset(new plugins_context("Multiplayer Configure"));
//These structure initializers create a lobby::process_data_event
plugins_context_->set_callback("launch", std::bind(&configure::plugin_event_helper, this, process_event_data (true, false)));
plugins_context_->set_callback("quit", std::bind(&configure::plugin_event_helper, this, process_event_data (false, true)));
plugins_context_->set_callback("set_name", [this](const config& cfg) { name_entry_.set_text(cfg["name"], font::NORMAL_COLOR); }, true);
if(!options_manager_.has_options() && engine_.force_lock_settings() && state_.classification().campaign_type != game_classification::CAMPAIGN_TYPE::MULTIPLAYER) {
set_result(CREATE);
}
}
configure::~configure()
{
try {
// Only save the settings if the dialog was 'accepted'
if(get_result() != CREATE) {
DBG_MP << "destructing multiplayer configure dialog - aborted game creation" << std::endl;
return;
}
DBG_MP << "destructing multiplayer configure dialog - a game will be configured" << std::endl;
// Save values for next game
DBG_MP << "storing parameter values in preferences" << std::endl;
preferences::set_shuffle_sides(engine_.shuffle_sides());
preferences::set_random_faction_mode(engine_.random_faction_mode().to_string());
preferences::set_use_map_settings(engine_.use_map_settings());
preferences::set_countdown(engine_.mp_countdown());
preferences::set_countdown_init_time(engine_.mp_countdown_init_time());
preferences::set_countdown_turn_bonus(engine_.mp_countdown_turn_bonus());
preferences::set_countdown_reservoir_time(engine_.mp_countdown_reservoir_time());
preferences::set_countdown_action_bonus(engine_.mp_countdown_action_bonus());
preferences::set_options(engine_.options());
// don't set observers preference if disabled (for singleplayer)
if (observers_game_.enabled())
preferences::set_allow_observers(engine_.allow_observers());
// don't set registered_users_only preference if disabled (for singleplayer)
if (registered_users_only_.enabled())
preferences::set_registered_users_only(engine_.registered_users_only());
// When using map settings, the following variables are determined by the map,
// so don't store them as the new preferences.
if(!engine_.use_map_settings()) {
preferences::set_fog(engine_.fog_game());
preferences::set_shroud(engine_.shroud_game());
preferences::set_turns(engine_.num_turns());
preferences::set_random_start_time(engine_.random_start_time());
preferences::set_village_gold(engine_.village_gold());
preferences::set_village_support(engine_.village_support());
preferences::set_xp_modifier(engine_.xp_modifier());
}
} catch (...) {}
}
void configure::get_parameters()
{
DBG_MP << "getting parameter values from widgets" << std::endl;
const int mp_countdown_turn_bonus_val = countdown_turn_bonus_slider_.value() <= countdown_turn_bonus_slider_.max_value() ?
countdown_turn_bonus_slider_.value() : -1;
const int mp_countdown_action_bonus_val = countdown_action_bonus_slider_.value() <= countdown_action_bonus_slider_.max_value() ?
countdown_action_bonus_slider_.value() : -1;
const int mp_countdown_reservoir_time_val = countdown_reservoir_time_slider_.value() <= countdown_reservoir_time_slider_.max_value() ?
countdown_reservoir_time_slider_.value() : -1;
int mp_countdown_init_time_val = countdown_init_time_slider_.value() <= countdown_init_time_slider_.max_value() ?
countdown_init_time_slider_.value() : -1;
if(mp_countdown_reservoir_time_val > 0 && mp_countdown_init_time_val > mp_countdown_reservoir_time_val)
mp_countdown_init_time_val = mp_countdown_reservoir_time_val;
// Updates the values in the configure_engine to match
// the values selected by the user with the widgets:
engine_.set_game_name(name_entry_.text());
// CHECK
engine_.set_mp_countdown_init_time(mp_countdown_init_time_val);
engine_.set_mp_countdown_turn_bonus(mp_countdown_turn_bonus_val);
engine_.set_mp_countdown_reservoir_time(mp_countdown_reservoir_time_val);
engine_.set_mp_countdown_action_bonus(mp_countdown_action_bonus_val);
engine_.set_mp_countdown(countdown_game_.checked());
if(nolock_settings_) {
const int num_turns_val = nolock_settings_->turns_slider_.value() <
nolock_settings_->turns_slider_.max_value() ? nolock_settings_->turns_slider_.value() : -1;
engine_.set_num_turns(num_turns_val);
engine_.set_village_gold(nolock_settings_->village_gold_slider_.value());
engine_.set_village_support(nolock_settings_->village_support_slider_.value());
engine_.set_xp_modifier(nolock_settings_->xp_modifier_slider_.value());
engine_.set_use_map_settings(nolock_settings_->use_map_settings_.checked());
engine_.set_random_start_time(nolock_settings_->random_start_time_.checked());
engine_.set_fog_game(nolock_settings_->fog_game_.checked());
engine_.set_shroud_game(nolock_settings_->shroud_game_.checked());
engine_.write_parameters();
}
engine_.set_allow_observers(observers_game_.checked());
engine_.set_registered_users_only(registered_users_only_.checked());
engine_.set_oos_debug(oos_debug_.checked());
engine_.set_shuffle_sides(shuffle_sides_.checked());
engine_.set_random_faction_mode(mp_game_settings::RANDOM_FACTION_MODE::from_int(random_faction_mode_.selected()));
engine_.set_options(options_manager_.get_values());
}
bool configure::plugin_event_helper(const process_event_data & data)
{
process_event_impl(data);
return get_result() == mp::ui::CONTINUE;
}
void configure::process_event()
{
int mousex, mousey;
SDL_GetMouseState(&mousex,&mousey);
tooltips::process(mousex, mousey);
process_event_data data;
data.launch = launch_game_.pressed();
data.quit = cancel_game_.pressed();
process_event_impl(data);
}
void configure::process_event_impl(const process_event_data & data)
{
if(data.quit) {
set_result(QUIT);
return;
}
if(data.launch) {
// check if the map is valid
if (name_entry_.text() == "") {
gui2::show_transient_message(video(), "", _("You must enter a name."));
} else {
set_result(CREATE);
return;
}
}
if(password_button_.pressed()) {
gui2::dialogs::mp_create_game_set_password::execute(
parameters_.password
, video());
}
if (entry_points_combo_.changed()) {
engine_.set_scenario(entry_points_combo_.selected());
force_use_map_settings_check_ = true;
}
std::stringstream buf;
if(nolock_settings_) {
// Turns per game
const int cur_turns = nolock_settings_->turns_slider_.value();
if(cur_turns < 100) {
buf << _("Turns: ") << cur_turns;
} else {
buf << _("Unlimited turns");
}
nolock_settings_->turns_label_.set_text(buf.str());
// Villages can produce between 1 and 5 gold a turn
const int village_gold = nolock_settings_->village_gold_slider_.value();
buf.str("");
buf << _("Village gold: ") << village_gold;
nolock_settings_->village_gold_label_.set_text(buf.str());
// Unit levels supported per village
const int village_support = nolock_settings_->village_support_slider_.value();
buf.str("");
buf << _("Village support: ") << village_support;
nolock_settings_->village_support_label_.set_text(buf.str());
// Experience modifier
const int xpmod = nolock_settings_->xp_modifier_slider_.value();
buf.str("");
buf << _("Experience modifier: ") << xpmod << "%";
nolock_settings_->xp_modifier_label_.set_text(buf.str());
if(nolock_settings_->use_map_settings_.pressed() || force_use_map_settings_check_) {
force_use_map_settings_check_ = false;
engine_.set_use_map_settings(nolock_settings_->use_map_settings_.checked());
// If the map settings are wanted use them,
// if not properly defined fall back to the default settings
nolock_settings_->turns_slider_.set_value(engine_.num_turns_default());
nolock_settings_->xp_modifier_slider_.set_value(engine_.xp_modifier_default());
nolock_settings_->random_start_time_.set_check(engine_.random_start_time_default());
nolock_settings_->village_gold_slider_.set_value(engine_.village_gold_default());
nolock_settings_->village_support_slider_.set_value(engine_.village_support_default());
nolock_settings_->fog_game_.set_check(engine_.fog_game_default());
nolock_settings_->shroud_game_.set_check(engine_.shroud_game_default());
// Set the widget states
nolock_settings_->turns_slider_.enable(!engine_.use_map_settings());
nolock_settings_->village_gold_slider_.enable(!engine_.use_map_settings());
nolock_settings_->village_support_slider_.enable(!engine_.use_map_settings());
nolock_settings_->xp_modifier_slider_.enable(!engine_.use_map_settings());
nolock_settings_->random_start_time_.enable(!engine_.use_map_settings());
nolock_settings_->fog_game_.enable(!engine_.use_map_settings());
nolock_settings_->shroud_game_.enable(!engine_.use_map_settings());
}
}
countdown_init_time_label_.enable(countdown_game_.checked());
countdown_init_time_slider_.enable(countdown_game_.checked());
countdown_turn_bonus_label_.enable(countdown_game_.checked());
countdown_turn_bonus_slider_.enable(countdown_game_.checked());
countdown_reservoir_time_label_.enable(countdown_game_.checked());
countdown_reservoir_time_slider_.enable(countdown_game_.checked());
countdown_action_bonus_label_.enable(countdown_game_.checked());
countdown_action_bonus_slider_.enable(countdown_game_.checked());
if(mp_countdown_init_time_ != countdown_init_time_slider_.value()
&& countdown_init_time_slider_.value() > countdown_reservoir_time_slider_.value())
{
countdown_reservoir_time_slider_.set_value(countdown_init_time_slider_.value());
}
if(mp_countdown_reservoir_time_ != countdown_reservoir_time_slider_.value()
&& countdown_reservoir_time_slider_.value() < countdown_init_time_slider_.value())
{
countdown_init_time_slider_.set_value(countdown_reservoir_time_slider_.value());
}
mp_countdown_init_time_ = countdown_init_time_slider_.value();
mp_countdown_reservoir_time_ = countdown_reservoir_time_slider_.value();
buf.str("");
buf << _("Init. limit: ") << mp_countdown_init_time_; // << _(" sec.");
countdown_init_time_label_.set_text(buf.str());
const int mp_countdown_turn_bonus_val = countdown_turn_bonus_slider_.value();
buf.str("");
buf << _("Turn bonus: ") << mp_countdown_turn_bonus_val; // << _(" sec.");
countdown_turn_bonus_label_.set_text(buf.str());
buf.str("");
buf << _("Reservoir: ") << mp_countdown_reservoir_time_; // << _(" sec.");
countdown_reservoir_time_label_.set_text(buf.str());
const int mp_countdown_action_bonus_val = countdown_action_bonus_slider_.value();
buf.str("");
buf << _("Action bonus: ") << mp_countdown_action_bonus_val; // << _(" sec.");
countdown_action_bonus_label_.set_text(buf.str());
options_manager_.process_event();
}
void configure::hide_children(bool hide)
{
DBG_MP << (hide ? "hiding" : "showing" ) << " children widgets" << std::endl;
ui::hide_children(hide);
if (nolock_settings_)
{
nolock_settings_->turns_slider_.hide(hide);
nolock_settings_->turns_label_.hide(hide);
nolock_settings_->village_gold_slider_.hide(hide);
nolock_settings_->village_gold_label_.hide(hide);
nolock_settings_->village_support_slider_.hide(hide);
nolock_settings_->village_support_label_.hide(hide);
nolock_settings_->xp_modifier_slider_.hide(hide);
nolock_settings_->xp_modifier_label_.hide(hide);
nolock_settings_->generic_label_.hide(hide);
nolock_settings_->use_map_settings_.hide(hide);
nolock_settings_->random_start_time_.hide(hide);
nolock_settings_->fog_game_.hide(hide);
nolock_settings_->shroud_game_.hide(hide);
}
countdown_init_time_slider_.hide(hide);
countdown_init_time_label_.hide(hide);
countdown_turn_bonus_slider_.hide(hide);
countdown_turn_bonus_label_.hide(hide);
countdown_reservoir_time_slider_.hide(hide);
countdown_reservoir_time_label_.hide(hide);
countdown_action_bonus_slider_.hide(hide);
countdown_action_bonus_label_.hide(hide);
countdown_game_.hide(hide);
name_entry_label_.hide(hide);
observers_game_.hide(hide);
registered_users_only_.hide(hide);
oos_debug_.hide(hide);
shuffle_sides_.hide(hide);
random_faction_mode_label_.hide(hide);
random_faction_mode_.hide(hide);
cancel_game_.hide(hide);
launch_game_.hide(hide);
password_button_.hide(hide);
name_entry_.hide(hide);
entry_points_label_.hide(hide);
entry_points_combo_.hide(hide);
options_pane_left_.hide(hide);
options_pane_right_.hide(hide);
options_manager_.hide_children(hide);
}
void configure::layout_children(const SDL_Rect& rect)
{
DBG_MP << "laying out the children" << std::endl;
ui::layout_children(rect);
const int border_size = 6;
const int column_border_size = 10;
SDL_Rect ca = client_area();
int xpos = ca.x;
int ypos = ca.y;
const int column_width = (ca.w - ca.x) / 2 - column_border_size;
// Dialog title
ypos += title().height() + border_size;
// Name Entry
name_entry_label_.set_location(xpos, ypos);
name_entry_.set_location(xpos + name_entry_label_.width() + border_size, ypos);
name_entry_.set_width(ca.w - name_entry_label_.width() - border_size);
ypos += std::max<int>(name_entry_.height(), name_entry_label_.height()) + border_size;
// Save ypos here (column top)
int ypos_columntop = ypos;
const int right_pane_height =
ca.h - (ypos_columntop - ca.y + launch_game_.height() + border_size);
// First column: non-gameplay settings
options_pane_left_.set_location(xpos, ypos);
options_pane_left_.set_width(column_width);
options_pane_left_.set_height(right_pane_height - entry_points_label_.height());
int slider_width = options_pane_left_.width() - 40;
int xpos_left = 0;
int ypos_left = 0;
ypos_left += 2 * border_size;
options_pane_left_.add_widget(&shuffle_sides_, xpos_left, ypos_left);
options_pane_left_.add_widget(&observers_game_,
xpos_left + options_pane_left_.width() / 3 + border_size, ypos_left);
options_pane_left_.add_widget(&registered_users_only_,
xpos_left + options_pane_left_.width() * 2/ 3 + border_size, ypos_left);
ypos_left += shuffle_sides_.height() + border_size;
options_pane_left_.add_widget(&random_faction_mode_label_, xpos_left, ypos_left);
xpos_left += random_faction_mode_label_.width() + border_size;
options_pane_left_.add_widget(&random_faction_mode_, xpos_left, ypos_left);
xpos_left += random_faction_mode_.width() + border_size;
if(!local_players_only_) {
options_pane_left_.add_widget(&password_button_,
std::max(xpos_left + 2* border_size, (options_pane_left_.width() / 2) + border_size), ypos_left);
} else {
password_button_.hide(true);
}
xpos_left = 0;
ypos_left += random_faction_mode_.height() + border_size;
options_pane_left_.add_widget(&countdown_game_, xpos_left, ypos_left);
ypos_left += countdown_game_.height() + border_size;
options_pane_left_.add_widget(&countdown_init_time_label_, xpos_left, ypos_left );
ypos_left += countdown_init_time_label_.height() + border_size;
countdown_init_time_slider_.set_width(slider_width);
options_pane_left_.add_widget(&countdown_init_time_slider_, xpos_left, ypos_left);
ypos_left += countdown_init_time_slider_.height() + border_size;
options_pane_left_.add_widget(&countdown_turn_bonus_label_, xpos_left, ypos_left);
ypos_left += countdown_turn_bonus_label_.height() + border_size;
countdown_turn_bonus_slider_.set_width(slider_width);
options_pane_left_.add_widget(&countdown_turn_bonus_slider_, xpos_left, ypos_left);
ypos_left += countdown_turn_bonus_slider_.height() + border_size;
options_pane_left_.add_widget(&countdown_reservoir_time_label_, xpos_left, ypos_left);
ypos_left += countdown_reservoir_time_label_.height() + border_size;
countdown_reservoir_time_slider_.set_width(slider_width);
options_pane_left_.add_widget(&countdown_reservoir_time_slider_, xpos_left, ypos_left);
ypos_left += countdown_reservoir_time_slider_.height() + border_size;
options_pane_left_.add_widget(&countdown_action_bonus_label_, xpos_left, ypos_left);
ypos_left += countdown_action_bonus_label_.height() + border_size;
countdown_action_bonus_slider_.set_width(slider_width);
options_pane_left_.add_widget(&countdown_action_bonus_slider_, xpos_left, ypos_left);
ypos_left += countdown_action_bonus_slider_.height() + border_size;
options_pane_left_.add_widget(&oos_debug_, xpos_left, ypos_left );
ypos_left += oos_debug_.height() + border_size;
if (show_entry_points_) {
int x = ca.x;
int y = ca.y + ca.h - entry_points_combo_.height();
entry_points_combo_.set_location(x, y);
y -= entry_points_label_.height() + border_size;
entry_points_label_.set_location(x, y);
}
// Second column: gameplay settings
xpos += column_width + column_border_size;
ypos = ypos_columntop;
options_pane_right_.set_location(xpos, ypos);
options_pane_right_.set_width(ca.w - (xpos - ca.x));
options_pane_right_.set_height(right_pane_height);
slider_width = options_pane_right_.width() - 40;
int xpos_right = 0;
int ypos_right = 0;
if(nolock_settings_)
{
options_pane_right_.add_widget(&nolock_settings_->generic_label_, xpos_right, ypos_right);
ypos_right += nolock_settings_->generic_label_.height() + border_size;
options_pane_right_.add_widget(&nolock_settings_->use_map_settings_, xpos_right, ypos_right);
options_pane_right_.add_widget(&nolock_settings_->fog_game_,
xpos_right + (options_pane_right_.width() - xpos_right)/2 + 5, ypos_right);
ypos_right += nolock_settings_->use_map_settings_.height() + border_size;
options_pane_right_.add_widget(&nolock_settings_->random_start_time_, xpos_right, ypos_right);
options_pane_right_.add_widget(&nolock_settings_->shroud_game_,
xpos_right + (options_pane_right_.width() - xpos_right)/2 + 5, ypos_right);
ypos_right += nolock_settings_->random_start_time_.height() + border_size;
options_pane_right_.add_widget(&nolock_settings_->turns_label_, xpos_right, ypos_right);
ypos_right += nolock_settings_->turns_label_.height() + border_size;
nolock_settings_->turns_slider_.set_width(slider_width);
options_pane_right_.add_widget(&nolock_settings_->turns_slider_, xpos_right, ypos_right);
ypos_right += nolock_settings_->turns_slider_.height() + border_size;
options_pane_right_.add_widget(&nolock_settings_->xp_modifier_label_, xpos_right, ypos_right);
ypos_right += nolock_settings_->xp_modifier_label_.height() + border_size;
nolock_settings_->xp_modifier_slider_.set_width(slider_width);
options_pane_right_.add_widget(&nolock_settings_->xp_modifier_slider_, xpos_right, ypos_right);
ypos_right += nolock_settings_->xp_modifier_slider_.height() + border_size;
options_pane_right_.add_widget(&nolock_settings_->village_support_label_, xpos_right, ypos_right);
ypos_right += nolock_settings_->village_support_label_.height() + border_size;
nolock_settings_->village_support_slider_.set_width(slider_width);
options_pane_right_.add_widget(&nolock_settings_->village_support_slider_, xpos_right, ypos_right);
ypos_right += nolock_settings_->village_support_slider_.height() + border_size;
options_pane_right_.add_widget(&nolock_settings_->village_gold_label_, xpos_right, ypos_right);
ypos_right += nolock_settings_->village_gold_label_.height() + border_size;
nolock_settings_->village_gold_slider_.set_width(slider_width);
options_pane_right_.add_widget(&nolock_settings_->village_gold_slider_, xpos_right, ypos_right);
ypos_right += nolock_settings_->village_gold_slider_.height() + 3 * border_size;
}
options_manager_.layout_widgets(xpos_right, ypos_right, column_width);
// OK / Cancel buttons
gui::button* left_button = &launch_game_;
gui::button* right_button = &cancel_game_;
#ifdef OK_BUTTON_ON_RIGHT
std::swap(left_button,right_button);
#endif
// Buttons
right_button->set_location(ca.x + ca.w - right_button->width(),
ca.y + ca.h - right_button->height());
left_button->set_location(right_button->location().x - left_button->width() -
gui::ButtonHPadding, ca.y + ca.h - left_button->height());
}
} // namespace mp

View file

@ -1,128 +0,0 @@
/*
Copyright (C) 2013 - 2016 Boldizsár Lipka <lipkab@zoho.com>
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 as published by
the Free Software Foundation; either version 2 of the License, 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.
*/
#ifndef MULTIPLAYER_CONFIGURE_HPP_INCLUDED
#define MULTIPLAYER_CONFIGURE_HPP_INCLUDED
#include "depcheck.hpp"
#include "mp_game_settings.hpp"
#include "multiplayer_ui.hpp"
#include "widgets/slider.hpp"
#include "widgets/scrollpane.hpp"
#include "widgets/combo.hpp"
#include "tooltips.hpp"
#include "mp_options.hpp"
#include "configure_engine.hpp"
class saved_game;
namespace mp {
class configure : public mp::ui
{
public:
///gives the user the option to adjust the passed saved_game
///Call get_parameters to finalize;
configure(CVideo& v, wesnothd_connection* connection, const config& game_config, chat& c, config& gamelist, saved_game& game, bool local_players_only);
~configure();
void get_parameters();
protected:
virtual void layout_children(const SDL_Rect& rect);
virtual void process_event();
virtual void hide_children(bool hide=true);
private:
//Settings that can be changed unledd wml forbids it
struct nolock_settings
{
nolock_settings(CVideo& video);
gui::slider turns_slider_;
gui::label turns_label_;
gui::slider village_gold_slider_;
gui::label village_gold_label_;
gui::slider village_support_slider_;
gui::label village_support_label_;
gui::slider xp_modifier_slider_;
gui::label xp_modifier_label_;
gui::label generic_label_;
gui::button use_map_settings_;
gui::button random_start_time_;
gui::button fog_game_;
gui::button shroud_game_;
};
bool local_players_only_;
tooltips::manager tooltip_manager_;
int mp_countdown_init_time_;
int mp_countdown_reservoir_time_;
gui::button countdown_game_;
gui::slider countdown_init_time_slider_;
gui::label countdown_init_time_label_;
gui::slider countdown_reservoir_time_slider_;
gui::label countdown_reservoir_time_label_;
gui::label countdown_turn_bonus_label_;
gui::slider countdown_turn_bonus_slider_;
gui::label countdown_action_bonus_label_;
gui::slider countdown_action_bonus_slider_;
gui::label name_entry_label_;
gui::button observers_game_;
gui::button registered_users_only_;
gui::button oos_debug_;
gui::button shuffle_sides_;
gui::label random_faction_mode_label_;
gui::combo random_faction_mode_;
gui::button cancel_game_;
gui::button launch_game_;
gui::button password_button_;
gui::textbox name_entry_;
gui::label entry_points_label_;
gui::combo entry_points_combo_;
gui::scrollpane options_pane_left_;
gui::scrollpane options_pane_right_;
std::vector<config const*> entry_points_;
bool show_entry_points_;
bool force_use_map_settings_check_;
saved_game& state_;
mp_game_settings& parameters_;
ng::configure_engine engine_;
options::manager options_manager_;
const std::unique_ptr<nolock_settings> nolock_settings_;
struct process_event_data {
bool launch, quit;
process_event_data()
: launch(false), quit(false)
{}
process_event_data(bool l, bool q)
: launch(l), quit(q)
{}
};
void process_event_impl(const process_event_data &);
bool plugin_event_helper(const process_event_data &);
};
} // end namespace mp
#endif

View file

@ -1,612 +0,0 @@
/*
Copyright (C) 2007 - 2016 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 as published by
the Free Software Foundation; either version 2 of the License, 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
* Prepare to join a multiplayer-game.
*/
#include "game_initialization/multiplayer_connect.hpp"
#include "ai/configuration.hpp"
#include "construct_dialog.hpp"
#include "display_chat_manager.hpp"
#include "font/standard_colors.hpp"
#include "game_preferences.hpp"
#include "gettext.hpp"
#include "log.hpp"
#include "map/map.hpp"
#include "mp_ui_alerts.hpp"
#include "scripting/plugins/context.hpp"
#include "wml_separators.hpp"
#include "utils/functional.hpp"
static lg::log_domain log_mp_connect("mp/connect");
#define DBG_MP LOG_STREAM(debug, log_mp_connect)
#define LOG_MP LOG_STREAM(info, log_mp_connect)
namespace mp {
std::vector<std::string> controller_options_names(
const std::vector<ng::controller_option>& controller_options)
{
std::vector<std::string> names;
for (const ng::controller_option& option : controller_options) {
names.push_back(option.second);
}
return names;
}
static std::string get_income_string(int income)
{
std::stringstream buf;
if (income < 0) {
buf << _("(") << income << _(")");
} else if (income > 0) {
buf << _("+") << income;
} else {
buf << _("Normal");
}
return buf.str();
}
connect::side::side(connect& parent, ng::side_engine_ptr engine) :
parent_(&parent),
engine_(engine),
gold_lock_(engine_->cfg()["gold_lock"].to_bool(
parent_->force_lock_settings())),
income_lock_(engine_->cfg()["income_lock"].to_bool(
parent_->force_lock_settings())),
team_lock_(engine_->cfg()["team_lock"].to_bool(
parent_->force_lock_settings())),
color_lock_(engine_->cfg()["color_lock"].to_bool(
parent_->force_lock_settings())),
changed_(false),
label_player_number_(parent.video(), std::to_string(engine_->index() + 1),
font::SIZE_LARGE, font::LOBBY_COLOR),
label_original_controller_(parent.video(), engine_->reserved_for(),
font::SIZE_SMALL),
label_gold_(parent.video(), std::to_string(engine_->gold()), font::SIZE_SMALL, font::LOBBY_COLOR),
label_income_(parent.video(), get_income_string(engine_->income()), font::SIZE_SMALL,
font::LOBBY_COLOR),
combo_controller_(new gui::combo_drag(parent.video(),
std::vector<std::string>(), parent.combo_control_group_)),
combo_ai_algorithm_(parent.video(), std::vector<std::string>()),
combo_faction_(parent.video(), std::vector<std::string>()),
label_leader_name_(parent.video(), engine_->cfg()["name"], font::SIZE_SMALL),
combo_leader_(parent.video(), std::vector<std::string>()),
combo_gender_(parent.video(), std::vector<std::string>()),
combo_team_(parent.video(), engine_->player_teams()),
combo_color_(parent.video(), engine->get_colors()),
slider_gold_(parent.video()),
slider_income_(parent.video())
{
DBG_MP << "initializing side" << std::endl;
slider_gold_.set_min(20);
slider_gold_.set_max(800);
slider_gold_.set_increment(25);
slider_gold_.set_value(engine_->cfg()["gold"].to_int(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(engine_->cfg()["income"]);
slider_income_.set_measurements(50, 16);
combo_faction_.enable(!parent_->params().saved_game);
combo_leader_.enable(!parent_->params().saved_game);
combo_gender_.enable(!parent_->params().saved_game);
combo_team_.enable(!parent_->params().saved_game);
combo_color_.enable(!parent_->params().saved_game);
slider_gold_.hide(parent_->params().saved_game);
slider_income_.hide(parent_->params().saved_game);
label_gold_.hide(parent_->params().saved_game);
label_income_.hide(parent_->params().saved_game);
if (parent_->params().use_map_settings) {
// Gold, income, team, and color are only suggestions.
// (Unless explicitly locked).
slider_gold_.enable(!gold_lock_);
label_gold_.enable(!gold_lock_);
slider_income_.enable(!income_lock_);
label_income_.enable(!income_lock_);
combo_team_.enable(!team_lock_);
combo_color_.enable(!color_lock_);
}
update_ui();
}
connect::side::side(const side& a) :
parent_(a.parent_),
engine_(a.engine_),
gold_lock_(a.gold_lock_),
income_lock_(a.income_lock_),
team_lock_(a.team_lock_),
color_lock_(a.color_lock_),
changed_(a.changed_),
label_player_number_(a.label_player_number_),
label_original_controller_(a.label_original_controller_),
label_gold_(a.label_gold_),
label_income_(a.label_income_),
combo_controller_(a.combo_controller_),
combo_ai_algorithm_(a.combo_ai_algorithm_),
combo_faction_(a.combo_faction_),
label_leader_name_(a.label_leader_name_),
combo_leader_(a.combo_leader_),
combo_gender_(a.combo_gender_),
combo_team_(a.combo_team_),
combo_color_(a.combo_color_),
slider_gold_(a.slider_gold_),
slider_income_(a.slider_income_)
{
}
connect::side::~side()
{
}
void connect::side::process_event()
{
int drop_target;
if ((drop_target = combo_controller_->get_drop_target()) > -1) {
if (engine_->swap_sides_on_drop_target(drop_target)) {
changed_ = true;
parent_->sides_[drop_target].changed_ = true;
update_ui();
parent_->sides_[drop_target].update_ui();
}
} else if (combo_controller_->changed() &&
combo_controller_->selected() >= 0) {
changed_ = engine_->controller_changed(combo_controller_->selected());
update_controller_ui();
}
if (combo_controller_->hidden()) {
combo_controller_->hide(false);
}
if (parent_->params().saved_game) {
return;
}
if (combo_color_.changed() && combo_color_.selected() >= 0) {
engine_->set_color(combo_color_.selected());
update_faction_combo();
engine_->flg().reset_leader_combo(combo_leader_, engine_->get_color());
engine_->flg().reset_gender_combo(combo_gender_, engine_->get_color());
changed_ = true;
}
if (combo_faction_.changed() && combo_faction_.selected() >= 0) {
engine_->flg().set_current_faction(combo_faction_.selected());
engine_->flg().reset_leader_combo(combo_leader_, engine_->get_color());
engine_->flg().reset_gender_combo(combo_gender_, engine_->get_color());
changed_ = true;
}
if (combo_ai_algorithm_.changed() && combo_ai_algorithm_.selected() >= 0) {
engine_->set_ai_algorithm(
parent_->ai_algorithms_[combo_ai_algorithm_.selected()]->id);
changed_ = true;
}
if (combo_leader_.changed() && combo_leader_.selected() >= 0) {
engine_->flg().set_current_leader(combo_leader_.selected());
engine_->flg().reset_gender_combo(combo_gender_, engine_->get_color());
changed_ = true;
}
if (combo_gender_.changed() && combo_gender_.selected() >= 0) {
engine_->flg().set_current_gender(combo_gender_.selected());
changed_ = true;
}
if (combo_team_.changed() && combo_team_.selected() >= 0) {
engine_->set_team(combo_team_.selected());
changed_ = true;
}
if (slider_gold_.value() != engine_->gold() && slider_gold_.enabled()) {
engine_->set_gold(slider_gold_.value());
changed_ = true;
label_gold_.set_text(std::to_string(engine_->gold()));
}
if (slider_income_.value() != engine_->income() && slider_income_.enabled()) {
engine_->set_income(slider_income_.value());
changed_ = true;
label_income_.set_text(get_income_string(engine_->income()));
}
}
bool connect::side::changed()
{
if (changed_) {
changed_ = false;
return true;
}
return false;
}
void connect::side::update_ui()
{
update_faction_combo();
engine_->flg().reset_leader_combo(combo_leader_, engine_->get_color());
engine_->flg().reset_gender_combo(combo_gender_, engine_->get_color());
update_controller_ui();
combo_team_.set_selected(engine_->team());
combo_color_.set_selected(engine_->color());
slider_gold_.set_value(engine_->gold());
label_gold_.set_text(std::to_string(engine_->gold()));
slider_income_.set_value(engine_->income());
std::stringstream buf;
if (engine_->income() < 0) {
buf << _("(") << engine_->income() << _(")");
} else if (engine_->income() > 0) {
buf << _("+") << engine_->income();
} else {
buf << _("Normal");
}
label_income_.set_text(buf.str());
}
void connect::side::add_widgets_to_scrollpane(gui::scrollpane& pane, int pos)
{
// Offset value to align labels in y-axis.
int label_y_offset = (combo_leader_.height() - label_leader_name_.height()) / 2;
pane.add_widget(&label_player_number_, 0, 5 + pos);
pane.add_widget(combo_controller_.get(), 20, 5 + pos);
pane.add_widget(&label_original_controller_, 20 +
(combo_controller_->width() - label_original_controller_.width()) / 2,
35 + pos + label_y_offset);
pane.add_widget(&combo_ai_algorithm_, 20, 35 + pos);
pane.add_widget(&combo_faction_, 135, 5 + pos);
pane.add_widget(&label_leader_name_, 135 +
(combo_faction_.width() - label_leader_name_.width()) / 2,
35 + pos + label_y_offset);
pane.add_widget(&combo_leader_, 250, 5 + pos);
pane.add_widget(&combo_gender_, 250, 35 + pos);
pane.add_widget(&combo_team_, 365, 5 + pos);
pane.add_widget(&combo_color_, 365, 35 + pos);
pane.add_widget(&slider_gold_, 475, 5 + pos);
pane.add_widget(&label_gold_, 475 + 5, 35 + pos + label_y_offset);
pane.add_widget(&slider_income_, 475 + slider_gold_.width(), 5 + pos);
pane.add_widget(&label_income_, 475 + 5 + slider_gold_.width(),
35 + pos + label_y_offset);
}
void connect::side::update_faction_combo()
{
std::vector<std::string> factions;
for (const config* faction : engine_->flg().choosable_factions()) {
const std::string& name = (*faction)["name"];
const std::string& icon = (*faction)["image"];
if (!icon.empty()) {
std::string rgb = (*faction)["flag_rgb"];
if (rgb.empty()) {
rgb = "magenta";
}
factions.push_back(IMAGE_PREFIX + icon + "~RC(" + rgb + ">" + engine_->get_color() + ")" + COLUMN_SEPARATOR + name);
} else {
factions.push_back(name);
}
}
combo_faction_.enable(factions.size() > 1 && !parent_->params().saved_game);
combo_faction_.set_items(factions);
combo_faction_.set_selected(engine_->flg().current_faction_index());
}
void connect::side::update_controller_ui()
{
// Update controllers combo.
combo_controller_->set_items(controller_options_names(
engine_->controller_options()));
combo_controller_->set_selected(engine_->current_controller_index());
combo_controller_->enable(engine_->controller_options().size() > 1);
// Update AI algorithm combo.
assert(!parent_->ai_algorithms_.empty());
int sel = 0;
int i = 0;
std::vector<std::string> ais;
for (const ai::description* desc : parent_->ai_algorithms_) {
ais.push_back(desc->text);
if (desc->id == engine_->ai_algorithm()) {
sel = i;
}
i++;
}
combo_ai_algorithm_.set_items(ais);
combo_ai_algorithm_.set_selected(sel);
// Ensures that the visually selected AI
// is the one that will be loaded.
engine_->set_ai_algorithm(parent_->ai_algorithms_[sel]->id);
// Adjust the visibility of AI algorithm combo
// and original controller label.
if (!parent_->hidden()) {
if (engine_->controller() == ng::CNTR_COMPUTER) {
// Computer selected, show AI combo.
combo_ai_algorithm_.hide(false);
label_original_controller_.hide(true);
} else {
// Computer de-selected, hide AI combo.
combo_ai_algorithm_.hide(true);
label_original_controller_.hide(false);
label_original_controller_.set_text(engine_->reserved_for());
}
} else {
combo_ai_algorithm_.hide(true);
}
}
connect::connect(CVideo& v, wesnothd_connection* connection, const std::string& game_name,
const config& game_config, chat& c, config& gamelist,
ng::connect_engine& engine) :
mp::ui(v, connection, _("Game Lobby: ") + game_name, game_config, c, gamelist),
ai_algorithms_(),
sides_(),
engine_(engine),
waiting_label_(video(), "", font::SIZE_SMALL, font::LOBBY_COLOR),
type_title_label_(video(), _("Player/Type"), font::SIZE_SMALL,
font::LOBBY_COLOR),
faction_name_title_label_(video(), _("Faction/Name"), font::SIZE_SMALL,
font::LOBBY_COLOR),
leader_gender_title_label_(video(), _("Leader/Gender"), font::SIZE_SMALL,
font::LOBBY_COLOR),
team_color_title_label_(video(), _("Team/Color"), font::SIZE_SMALL,
font::LOBBY_COLOR),
gold_title_label_(video(), _("Gold"), font::SIZE_SMALL,
font::LOBBY_COLOR),
income_title_label_(video(), _("Income"), font::SIZE_SMALL,
font::LOBBY_COLOR),
scroll_pane_(video()),
launch_(video(), _("Im Ready")),
cancel_(video(), engine_.first_scenario() ? _("Cancel") : _("Quit")),
combo_control_group_(new gui::drop_group_manager())
{
DBG_MP << "setting up connect dialog" << std::endl;
// Load game exception occured.
if (engine_.level().empty()) {
set_result(CREATE);
return;
}
if (get_result() == QUIT || get_result() == CREATE) {
return;
}
if (engine_.scenario()["id"].empty()) {
throw config::error(_("The scenario is invalid because it has no id."));
}
ai_algorithms_ = ai::configuration::get_available_ais();
// Sides.
for (ng::side_engine_ptr s : engine_.side_engines()) {
sides_.push_back(side(*this, s));
}
if (sides_.empty() && !game_config::debug) {
throw config::error(
_("The scenario is invalid because it has no sides."));
}
// Add side widgets to scroll pane.
int side_pos_y_offset = 0;
for (side& s : sides_) {
if (!s.engine()->allow_player() && !game_config::debug) {
continue;
}
s.add_widgets_to_scrollpane(scroll_pane_, side_pos_y_offset);
side_pos_y_offset += 60;
}
append_to_title("" + engine_.scenario()["name"].t_str());
gold_title_label_.hide(params().saved_game);
income_title_label_.hide(params().saved_game);
update_playerlist_state(true);
plugins_context_.reset(new plugins_context("Multiplayer Connect"));
//These structure initializers create a lobby::process_data_event
plugins_context_->set_callback("launch", std::bind(&connect::plugin_event_helper, this, process_event_data (true, false)));
plugins_context_->set_callback("quit", std::bind(&connect::plugin_event_helper, this, process_event_data (false, true)));
plugins_context_->set_callback("chat", [this](const config& cfg) { send_chat_message(cfg["message"], false); }, true);
}
connect::~connect()
{
}
bool connect::plugin_event_helper(const process_event_data & data)
{
process_event_impl(data);
return get_result() == mp::ui::CONTINUE;
}
void connect::process_event()
{
process_event_data data;
data.quit = cancel_.pressed();
data.launch = launch_.pressed();
process_event_impl(data);
}
void connect::process_event_impl(const process_event_data & data)
{
bool changed = false;
for (side& s : sides_) {
s.process_event();
if (s.changed()) {
changed = true;
}
}
if (data.quit) {
if (wesnothd_connection_) {
config cfg;
cfg.add_child("leave_game");
send_to_server(cfg);
}
set_result(QUIT);
return;
}
if (data.launch) {
if (engine_.can_start_game()) {
set_result(mp::ui::PLAY);
}
}
// If something has changed in the level config,
// send it to the network.
if (changed) {
update_playerlist_state();
engine_.update_and_send_diff();
}
}
void connect::hide_children(bool hide)
{
DBG_MP << (hide ? "hiding" : "showing" ) <<
" children widgets" << std::endl;
ui::hide_children(hide);
waiting_label_.hide(hide);
scroll_pane_.hide(hide); // Scroll pane contents are automatically hidden.
faction_name_title_label_.hide(hide);
leader_gender_title_label_.hide(hide);
team_color_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::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());
type_title_label_.set_location(left + 30, top + 35);
faction_name_title_label_.set_location((left + 145), top + 35);
leader_gender_title_label_.set_location((left + 260), top + 35);
team_color_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);
waiting_label_.set_location(left + gui::ButtonHPadding,
bottom - left_button->height() + 4);
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::process_network_data(const config& data)
{
ui::process_network_data(data);
bool was_able_to_start = engine_.can_start_game();
std::pair<bool, bool> result = engine_.process_network_data(data);
if (result.first) {
set_result(QUIT);
}
for (side& s : sides_) {
s.update_ui();
}
update_playerlist_state(result.second); //result.second is the silent flag
if (!was_able_to_start && engine_.can_start_game()) {
DBG_MP << "play party full sound" << std::endl;
mp_ui_alerts::ready_for_start();
}
}
void connect::update_playerlist_state(bool silent)
{
DBG_MP << "updating player list state" << std::endl;
waiting_label_.set_text(engine_.can_start_game() ? ""
: engine_.sides_available()
? _("Waiting for players to join...")
: _("Waiting for players to choose factions..."));
launch_.enable(engine_.can_start_game());
// If the "gamelist_" variable has users, use it.
// Else, extracts the user list from the actual player list.
if (gamelist().child("user")) {
ui::gamelist_updated(silent);
} else {
// Updates the player list
std::vector<std::string> playerlist;
for (const std::string& user : engine_.connected_users()) {
playerlist.push_back(user);
}
set_user_list(playerlist, silent);
set_user_menu_items(playerlist);
}
}
} // end namespace mp

View file

@ -1,160 +0,0 @@
/*
Copyright (C) 2007 - 2016 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 as published by
the Free Software Foundation; either version 2 of the License, 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 */
#ifndef MULTIPLAYER_CONNECT_H_INCLUDED
#define MULTIPLAYER_CONNECT_H_INCLUDED
#include "commandline_options.hpp"
#include "connect_engine.hpp"
#include "multiplayer_ui.hpp"
#include "widgets/combo_drag.hpp"
#include "widgets/scrollpane.hpp"
#include "widgets/slider.hpp"
class CVideo;
namespace ai {
struct description;
}
namespace mp {
// Helper function to retrieve controller names.
std::vector<std::string> controller_options_names(
const std::vector<ng::controller_option>& controller_options);
class connect : public mp::ui
{
public:
class side {
public:
side(connect& parent, ng::side_engine_ptr engine);
side(const side& a);
~side();
void process_event();
// Returns true if this side changed since last call to this method.
bool changed();
void update_ui();
void add_widgets_to_scrollpane(gui::scrollpane& pane, int pos);
ng::side_engine_ptr engine() { return engine_; }
const ng::side_engine_ptr engine() const { return engine_; }
private:
// Update UI methods and their helper(s).
void update_faction_combo();
void update_controller_ui();
// The mp::connect widget owning this mp::connect::side.
connect* parent_;
ng::side_engine_ptr engine_;
// Flags for controlling which configuration widgets should be locked.
bool gold_lock_;
bool income_lock_;
bool team_lock_;
bool color_lock_;
bool changed_;
gui::label label_player_number_;
gui::label label_original_controller_;
gui::label label_gold_;
gui::label label_income_;
gui::combo_drag_ptr combo_controller_;
gui::combo combo_ai_algorithm_;
gui::combo combo_faction_;
gui::label label_leader_name_;
gui::combo combo_leader_;
gui::combo combo_gender_;
gui::combo combo_team_;
gui::combo combo_color_;
gui::slider slider_gold_;
gui::slider slider_income_;
};
typedef std::vector<side> side_list;
connect(CVideo& v, wesnothd_connection* connection, const std::string& game_name,
const config& game_config, chat& c, config& gamelist,
ng::connect_engine& engine);
~connect();
// Updates the current game state, resolves random factions, and sends a
// "start game" message to the network.
void start_game() { engine_.start_game(); }
void start_game_commandline(const commandline_options& cmdline_opts)
{ engine_.start_game_commandline(cmdline_opts); }
protected:
virtual void process_event();
virtual void layout_children(const SDL_Rect& rect);
virtual void hide_children(bool hide = true);
virtual void process_network_data(const config& data);
private:
connect(const connect&);
void operator=(const connect&);
// Updates the state of the player list, the launch button and of the start
// game label, to reflect the actual state.
void update_playerlist_state(bool silent = true);
const mp_game_settings& params() { return engine_.params(); }
bool force_lock_settings() const { return engine_.force_lock_settings(); }
std::vector<ai::description*> ai_algorithms_;
side_list sides_;
ng::connect_engine& engine_;
gui::label waiting_label_;
gui::label type_title_label_;
gui::label faction_name_title_label_;
gui::label leader_gender_title_label_;
gui::label team_color_title_label_;
gui::label gold_title_label_;
gui::label income_title_label_;
gui::scrollpane scroll_pane_;
gui::button launch_;
gui::button cancel_;
gui::drop_group_manager_ptr combo_control_group_;
struct process_event_data {
bool launch, quit;
process_event_data()
: launch(false), quit(false)
{}
process_event_data(bool l, bool q)
: launch(l), quit(q)
{}
};
void process_event_impl(const process_event_data &);
bool plugin_event_helper(const process_event_data &);
};
} // end namespace mp
#endif

View file

@ -1,783 +0,0 @@
/*
Copyright (C) 2007 - 2016 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 as published by
the Free Software Foundation; either version 2 of the License, 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
* Create a multiplayer-game: select map, players, options etc.
*/
#include "global.hpp"
#include "gettext.hpp"
#include "game_config_manager.hpp"
#include "video.hpp"
#include "game_preferences.hpp"
#include "config_assign.hpp"
#include "construct_dialog.hpp"
#include "settings.hpp"
#include "map/map.hpp"
#include "map/exception.hpp"
#include "generators/map_create.hpp"
#include "gui/dialogs/message.hpp"
#include "gui/dialogs/campaign_difficulty.hpp"
#include "gui/dialogs/multiplayer/mp_create_game_set_password.hpp"
#include "gui/dialogs/transient_message.hpp"
#include "gui/widgets/window.hpp"
#include "minimap.hpp"
#include "game_initialization/multiplayer_create.hpp"
#include "filesystem.hpp"
#include "savegame.hpp"
#include "saved_game.hpp"
#include "scripting/plugins/context.hpp"
#include "log.hpp"
#include "wml_exception.hpp"
#include "wml_separators.hpp"
#include "formula/string_utils.hpp"
#include "widgets/multimenu.hpp"
#include "sdl/utils.hpp"
#include "sdl/rect.hpp"
#include "font/standard_colors.hpp"
#include "utils/functional.hpp"
static lg::log_domain log_config("config");
#define ERR_CF LOG_STREAM(err, log_config)
static lg::log_domain log_mp_create("mp/create");
#define DBG_MP LOG_STREAM(debug, log_mp_create)
namespace {
const SDL_Rect null_rect = {0, 0, 0, 0};
}
namespace mp {
static config get_selected_helper(const ng::create_engine * eng_ptr)
{
assert(eng_ptr);
const ng::create_engine & eng = *eng_ptr;
return config_of("id", eng.current_level().id())
("name", eng.current_level().name())
("icon", eng.current_level().icon())
("description", eng.current_level().description())
("allow_era_choice", eng.current_level().allow_era_choice())
("type", eng.current_level_type());
}
static config find_helper(const ng::create_engine * eng_ptr, const config & cfg)
{
assert(eng_ptr);
const ng::create_engine & eng = *eng_ptr;
std::string str = cfg["id"].str();
return config_of("index", eng.find_level_by_id(str))("type", eng.find_level_type_by_id(str));
}
create::create(CVideo& video, wesnothd_connection* connection, const config& cfg, saved_game& state, chat& c, config& gamelist) :
ui(video, connection, _("Create Game"), cfg, c, gamelist),
tooltip_manager_(video),
era_selection_(-1),
mod_selection_(-1),
level_selection_(-1),
eras_menu_(video, std::vector<std::string>()),
levels_menu_(video, std::vector<std::string>()),
mods_menu_(video, std::vector<std::string>()),
filter_name_label_(video, _("Filter:"), font::SIZE_SMALL, font::LOBBY_COLOR),
filter_num_players_label_(video, _("Number of players: any"), font::SIZE_SMALL, font::LOBBY_COLOR),
map_generator_label_(video, _("Random map options:"), font::SIZE_SMALL, font::LOBBY_COLOR),
era_label_(video, _("Era:"), font::SIZE_SMALL, font::LOBBY_COLOR),
no_era_label_(video, _("No eras available\nfor this game."),
font::SIZE_SMALL, font::LOBBY_COLOR),
mod_label_(video, _("Modifications:"), font::SIZE_SMALL, font::LOBBY_COLOR),
map_size_label_(video, "", font::SIZE_SMALL, font::LOBBY_COLOR),
num_players_label_(video, "", font::SIZE_SMALL, font::LOBBY_COLOR),
level_type_label_(video, "Game type:", font::SIZE_SMALL, font::LOBBY_COLOR),
launch_game_(video, _("Next")),
cancel_game_(video, _("Cancel")),
regenerate_map_(video, _("Regenerate")),
generator_settings_(video, _("Settings...")),
load_game_(video, _("Load Game...")),
level_type_combo_(video, std::vector<std::string>()),
filter_num_players_slider_(video),
description_(video, 100, "", false),
filter_name_(video, 100, "", true, 256, font::SIZE_SMALL),
image_restorer_(nullptr),
image_rect_(null_rect),
available_level_types_(),
engine_(video, state)
{
filter_num_players_slider_.set_min(1);
filter_num_players_slider_.set_max(9);
filter_num_players_slider_.set_increment(1);
DBG_MP << "constructing multiplayer create dialog" << std::endl;
levels_menu_.set_numeric_keypress_selection(false);
typedef std::pair<ng::level::TYPE, std::string> level_type_info;
std::vector<level_type_info> all_level_types;
all_level_types.push_back(std::make_pair(ng::level::TYPE::SCENARIO, _("Scenarios")));
all_level_types.push_back(std::make_pair(ng::level::TYPE::CAMPAIGN, _("Campaigns")));
all_level_types.push_back(std::make_pair(ng::level::TYPE::USER_MAP, _("User Maps")));
all_level_types.push_back(std::make_pair(ng::level::TYPE::USER_SCENARIO, _("User Scenarios")));
all_level_types.push_back(std::make_pair(ng::level::TYPE::RANDOM_MAP, _("Random Maps")));
if (game_config::debug) {
all_level_types.push_back(std::make_pair(ng::level::TYPE::SP_CAMPAIGN,
"SP Campaigns"));
}
std::vector<std::string> combo_level_names;
for (level_type_info type_info : all_level_types) {
if (!engine_.get_levels_by_type_unfiltered(type_info.first).empty()) {
available_level_types_.push_back(type_info.first);
combo_level_names.push_back(type_info.second);
}
}
if (available_level_types_.empty()) {
gui2::show_transient_message(video, "", _("No games found."));
throw game::error(_("No games found."));
}
level_type_combo_.set_items(combo_level_names);
size_t combo_new_selection = 0;
size_t level_new_selection = 0;
// Set level selection according to the preferences, if possible.
size_t type_index = 0;
for (ng::level::TYPE type : available_level_types_) {
if (preferences::level_type() == type.cast<int>()) {
break;
}
type_index++;
}
if (type_index < available_level_types_.size()) {
combo_new_selection = type_index;
int level_index = engine_.find_level_by_id(preferences::level());
if (level_index != -1) {
level_new_selection = level_index;
}
}
level_type_combo_.set_selected(combo_new_selection);
init_level_type_changed(level_new_selection);
const std::vector<std::string>& era_names =
engine_.extras_menu_item_names(ng::create_engine::ERA);
if(era_names.empty()) {
gui2::show_transient_message(video, "", _("No eras found."));
throw config::error(_("No eras found"));
}
eras_menu_.set_items(era_names);
// Set era selection according to the preferences, if possible.
int era_new_selection = engine_.find_extra_by_id(ng::create_engine::ERA,
preferences::era());
eras_menu_.move_selection((era_new_selection != -1) ? era_new_selection : 0);
std::vector<std::string> mods = engine_.extras_menu_item_names(ng::create_engine::MOD);
mods_menu_.set_items(mods);
mods_menu_.move_selection(0);
// don't set 0 explicitly, because move_selection(0) may fail if there's
// no modifications at all
mod_selection_ = mods_menu_.selection();
if (mod_selection_ == -1) {
mod_label_.set_text(_("Modifications:\nNone found."));
}
gamelist_updated();
plugins_context_.reset(new plugins_context("Multiplayer Create"));
//These structure initializers create a lobby::process_data_event
plugins_context_->set_callback("create", std::bind(&create::plugin_event_helper, this, process_event_data (true, false, false)));
plugins_context_->set_callback("load", std::bind(&create::plugin_event_helper, this, process_event_data (false, true, false)));
plugins_context_->set_callback("quit", std::bind(&create::plugin_event_helper, this, process_event_data (false, false, true)));
plugins_context_->set_callback("chat", [this](const config& cfg) { send_chat_message(cfg["message"], false); }, true);
plugins_context_->set_callback("select_level", [this](const config& cfg) { levels_menu_.move_selection(cfg["index"].to_int()); }, true);
plugins_context_->set_callback("select_type", [this](const config& cfg) { select_level_type_helper(cfg["type"]); }, true);
plugins_context_->set_accessor("game_config", std::bind(&create::game_config, this));
plugins_context_->set_accessor("get_selected", std::bind(&get_selected_helper, &engine_));
plugins_context_->set_accessor("find_level", std::bind(&find_helper, &engine_, _1));
}
void create::select_level_type_helper(const std::string & str)
{
for (size_t idx = 0; idx < available_level_types_.size(); idx++) {
if (available_level_types_[idx].to_string() == str) {
level_type_combo_.set_selected(idx);
init_level_type_changed(0);
process_event_impl(process_event_data(false, false, false));
}
}
}
create::~create()
{
try {
// Only save the settings if the dialog was 'accepted'
if(get_result() != CREATE) {
DBG_MP << "destructing multiplayer create dialog - aborted game creation" << std::endl;
return;
}
DBG_MP << "destructing multiplayer create dialog - a game will be created" << std::endl;
// Save values for next game
DBG_MP << "storing parameter values in preferences" << std::endl;
preferences::set_era(engine_.current_extra(ng::create_engine::ERA).id);
preferences::set_level(engine_.current_level().id());
preferences::set_level_type(engine_.current_level_type().cast<int>());
preferences::set_modifications(engine_.active_mods());
} catch (...) {}
}
const mp_game_settings& create::get_parameters()
{
return engine_.get_parameters();
}
bool create::plugin_event_helper(const process_event_data & data)
{
process_event_impl(data);
return get_result() == mp::ui::CONTINUE;
}
void create::process_event()
{
int mousex, mousey;
SDL_GetMouseState(&mousex,&mousey);
tooltips::process(mousex, mousey);
process_event_data data;
data.quit = cancel_game_.pressed();
data.create = launch_game_.pressed() || levels_menu_.double_clicked();
data.load = load_game_.pressed();
process_event_impl(data);
}
void create::process_event_impl(const process_event_data & data)
{
if (data.quit) {
set_result(QUIT);
return;
}
if (data.create) {
if (engine_.current_level().can_launch_game()) {
engine_.prepare_for_era_and_mods();
if (engine_.current_level_type() == ng::level::TYPE::CAMPAIGN ||
engine_.current_level_type() == ng::level::TYPE::SP_CAMPAIGN) {
std::string difficulty = engine_.select_campaign_difficulty();
if (difficulty == "CANCEL") {
return;
}
engine_.prepare_for_campaign(difficulty);
}
else if (engine_.current_level_type() == ng::level::TYPE::SCENARIO)
{
engine_.prepare_for_scenario();
}
else
{
//This means define= doesn't work for random generated scenarios
engine_.prepare_for_other();
}
engine_.prepare_for_new_level();
set_result(CREATE);
return;
} else {
gui2::show_transient_message(video(), "",
_("The level is invalid."));
}
}
if (level_type_combo_.changed()) {
init_level_type_changed(0);
}
if (data.load) {
try
{
savegame::loadgame load(video(),
game_config_manager::get()->game_config(), engine_.get_state());
if (data.filename) {
load.data().filename = *data.filename;
if (!load.load_game()) {
return ;
}
} else {
if (!load.load_multiplayer_game()) {
return ;
}
}
if (load.data().cancel_orders)
engine_.get_state().cancel_orders();
engine_.prepare_for_saved_game();
set_result(LOAD_GAME);
return;
}
catch(config::error&) {
}
}
if (mod_selection_ != mods_menu_.selection()) {
mod_selection_ = mods_menu_.selection();
engine_.set_current_mod_index(mod_selection_);
set_description(engine_.current_extra(ng::create_engine::MOD).description);
}
int changed_mod = mods_menu_.last_changed();
if (changed_mod != -1) {
engine_.set_current_mod_index(changed_mod);
engine_.toggle_current_mod();
synchronize_selections();
}
bool era_changed = era_selection_ != eras_menu_.selection();
era_selection_ = eras_menu_.selection();
if (era_changed) {
engine_.set_current_era_index(era_selection_);
set_description(engine_.current_extra(ng::create_engine::ERA).description);
synchronize_selections();
}
if (filter_name_.text() != engine_.level_name_filter()) {
engine_.apply_level_filter(filter_name_.text());
init_level_type_changed(0);
}
bool level_changed = level_selection_ != levels_menu_.selection();
level_selection_ = levels_menu_.selection();
if (level_changed && level_selection_ >= 0) {
init_level_changed(level_selection_);
synchronize_selections();
}
if (engine_.generator_assigned() && generator_settings_.pressed()) {
engine_.generator_user_config();
level_changed = true;
}
if(engine_.generator_assigned() &&
(level_changed || regenerate_map_.pressed())) {
const cursor::setter cursor_setter(cursor::WAIT);
cursor::setter cur(cursor::WAIT);
engine_.init_generated_level_data();
if (!engine_.current_level().data()["error_message"].empty())
gui2::show_message(video(), "map generation error",
engine_.current_level().data()["error_message"]);
level_changed = true;
}
if(level_changed) {
std::stringstream players;
std::stringstream map_size;
players << _("Players: ");
engine_.current_level().set_metadata();
draw_level_image();
set_description(engine_.current_level().description());
switch (engine_.current_level_type().v) {
case ng::level::TYPE::SCENARIO:
case ng::level::TYPE::USER_MAP:
case ng::level::TYPE::USER_SCENARIO:
case ng::level::TYPE::RANDOM_MAP: {
ng::scenario* current_scenario =
dynamic_cast<ng::scenario*>(&engine_.current_level());
assert(current_scenario);
players << current_scenario->num_players();
map_size << _("Size: ") << current_scenario->map_size();
break;
}
case ng::level::TYPE::CAMPAIGN:
case ng::level::TYPE::SP_CAMPAIGN: {
ng::campaign* current_campaign =
dynamic_cast<ng::campaign*>(&engine_.current_level());
assert(current_campaign);
players << current_campaign->min_players();
if (current_campaign->max_players() !=
current_campaign->min_players()) {
players << " to " << current_campaign->max_players();
}
break;
}
} // end switch
map_size_label_.set_text(map_size.str());
num_players_label_.set_text(players.str());
launch_game_.enable(engine_.current_level().can_launch_game());
generator_settings_.enable(engine_.generator_assigned());
regenerate_map_.enable(engine_.generator_assigned());
}
if (filter_num_players_slider_.value() != engine_.player_num_filter()) {
const int val = filter_num_players_slider_.value();
engine_.apply_level_filter(val);
std::stringstream ss;
if (val == 1) {
ss << _("Number of players: any");
} else {
ss << _("Number of players: ") << val;
}
filter_num_players_label_.set_text(ss.str());
init_level_type_changed(0);
}
}
void create::init_level_type_changed(size_t index)
{
int selected = level_type_combo_.selected();
if (selected < 0) {
selected = 0;
}
engine_.set_current_level_type(available_level_types_[selected]);
const std::vector<std::string>& menu_item_names =
engine_.levels_menu_item_names();
init_level_changed((index < menu_item_names.size()) ? index : 0);
levels_menu_.set_items(menu_item_names);
levels_menu_.move_selection(index);
level_selection_ = -1;
}
void create::init_level_changed(size_t index)
{
engine_.set_current_level(index);
// N.B. the order of hide() calls here is important
// to avoid redrawing glitches.
if (engine_.current_level().allow_era_choice()) {
no_era_label_.hide(true);
eras_menu_.hide(false);
} else {
eras_menu_.hide(true);
no_era_label_.hide(false);
}
}
void create::synchronize_selections()
{
DBG_MP << "Synchronizing with the dependency manager" << std::endl;
if (era_selection_ != engine_.dependency_manager().get_era_index()) {
eras_menu_.move_selection(engine_.dependency_manager().get_era_index());
process_event();
}
if (engine_.current_level_type() != ng::level::TYPE::CAMPAIGN &&
engine_.current_level_type() != ng::level::TYPE::SP_CAMPAIGN) {
if (engine_.current_level().id() !=
engine_.dependency_manager().get_scenario()) {
// Match scenario and scenario type
ng::level::TYPE level_type_at_index;
int index = engine_.find_level_by_id(
engine_.dependency_manager().get_scenario());
size_t type_index;
if (index == -1) {
return;
}
level_type_at_index = engine_.find_level_type_by_id(
engine_.dependency_manager().get_scenario());
engine_.set_current_level_type(level_type_at_index);
init_level_changed(index);
levels_menu_.set_items(engine_.levels_menu_item_names());
levels_menu_.move_selection(index);
type_index = 0;
for (ng::level::TYPE type : available_level_types_) {
if (level_type_at_index == type) {
level_type_combo_.set_selected(type_index);
break;
}
type_index++;
}
}
process_event();
}
engine_.init_active_mods();
update_mod_menu();
}
void create::draw_level_image()
{
surface image(
engine_.current_level().create_image_surface(image_rect_));
if (!image.null()) {
SDL_Color back_color = {0,0,0,SDL_ALPHA_OPAQUE};
draw_centered_on_background(image, image_rect_, back_color,
video().getSurface());
} else {
surface& display(video().getSurface());
sdl::fill_rect(display, &image_rect_,
SDL_MapRGB(display->format, 0, 0, 0));
update_rect(image_rect_);
}
}
void create::set_description(const std::string& description)
{
description_.set_text(description.empty() ? _("No description available.") :
description);
}
void create::update_mod_menu()
{
for (size_t i = 0; i<mods_menu_.number_of_items(); i++) {
mods_menu_.set_active(i, engine_.dependency_manager().is_modification_active(i));
}
}
void create::hide_children(bool hide)
{
DBG_MP << (hide ? "hiding" : "showing" ) << " children widgets" << std::endl;
ui::hide_children(hide);
eras_menu_.hide(hide || !engine_.current_level().allow_era_choice());
no_era_label_.hide(hide || engine_.current_level().allow_era_choice());
levels_menu_.hide(hide);
mods_menu_.hide(hide);
filter_name_.hide(hide);
filter_num_players_label_.hide(hide);
map_generator_label_.hide(hide);
map_size_label_.hide(hide);
era_label_.hide(hide);
mod_label_.hide(hide);
num_players_label_.hide(hide);
level_type_label_.hide(hide);
level_type_combo_.hide(hide);
cancel_game_.hide(hide);
launch_game_.hide(hide);
load_game_.hide(hide);
regenerate_map_.hide(hide);
generator_settings_.hide(hide);
filter_num_players_slider_.hide(hide);
description_.hide(hide);
filter_name_.hide(hide);
if (hide) {
image_restorer_.reset(nullptr);
} else {
image_restorer_.reset(new surface_restorer(&video(), image_rect_));
engine_.current_level().set_metadata();
draw_level_image();
}
}
void create::layout_children(const SDL_Rect& rect)
{
DBG_MP << "laying out the children" << std::endl;
ui::layout_children(rect);
const int border_size = 6;
const int column_border_size = 10;
SDL_Rect ca = client_area();
int xpos = ca.x;
int ypos = ca.y;
// 222 is two times a button's minimal width plus one time border_size.
// Instead of binding this value to the actual button widths, I chose this
// because it makes no difference for most languages, and where it does, I
// guess we'd prefer having the buttons less neatly aligned to having a
// potentially giant image.
const int image_width = ca.h < 500 ? 111 : 222;
const int menu_width = (ca.w - 3 * column_border_size - image_width) / 3;
const int eras_menu_height = (ca.h / 2 - era_label_.height() -
2 * border_size - cancel_game_.height());
const int mods_menu_height = (ca.h / 2 - mod_label_.height() -
3 * border_size - cancel_game_.height());
// Dialog title
ypos += title().height() + border_size;
// Save ypos here (column top)
int ypos_columntop = ypos;
// First column: image & random map options
image_rect_ = sdl::create_rect(xpos, ypos, image_width, image_width);
ypos += image_width + border_size;
num_players_label_.set_location(xpos, ypos);
ypos += num_players_label_.height() + border_size;
map_size_label_.set_location(xpos, ypos);
ypos += map_size_label_.height() + 2 * border_size;
const int ypos1 = ypos;
const int xpos1 = xpos;
// The description box is set later
// Second column: filtering options
ypos = ypos_columntop;
xpos += image_width + column_border_size;
filter_name_label_.set_location(xpos, ypos);
filter_name_.set_location(xpos + filter_name_label_.width() + border_size, ypos);
filter_name_.set_measurements(menu_width - border_size - filter_name_label_.width(), filter_name_label_.height());
ypos += filter_name_.height() + border_size;
filter_num_players_label_.set_location(xpos, ypos);
ypos += filter_num_players_label_.height() + border_size;
filter_num_players_slider_.set_location(xpos, ypos);
filter_num_players_slider_.set_width(menu_width);
ypos += filter_num_players_slider_.height() + border_size;
map_generator_label_.set_location(xpos, ypos);
ypos += map_generator_label_.height() + border_size;
regenerate_map_.set_location(xpos, ypos);
ypos += regenerate_map_.height() + border_size;
generator_settings_.set_location(xpos, ypos);
ypos += generator_settings_.height() + border_size;
load_game_.set_location(xpos, ypos + 4 * border_size);
// And now the description box
description_.set_location(xpos1, std::max(ypos,ypos1));
description_.set_measurements(image_width + border_size + menu_width, ca.h + ca.y - std::max(ypos,ypos1) - border_size);
description_.set_wrap(true);
ypos += description_.height() + border_size;
//Third column: levels menu
ypos = ypos_columntop;
xpos += menu_width + column_border_size;
level_type_label_.set_location(xpos, ypos);
ypos += level_type_label_.height() + border_size;
level_type_combo_.set_location(xpos, ypos);
ypos += level_type_combo_.height() + border_size;
const int levels_menu_y_offset = (ca.w < 900 || ca.h < 500) ?
((cancel_game_.height() + border_size) * -1) : 0;
levels_menu_.set_max_width(menu_width);
levels_menu_.set_max_height(ca.h + ca.y - ypos + levels_menu_y_offset);
levels_menu_.set_location(xpos, ypos);
// Menu dimensions are only updated when items are set. So do this now.
int levelsel = levels_menu_.selection();
levels_menu_.set_items(engine_.levels_menu_item_names());
levels_menu_.move_selection(levelsel);
// Place game type combo and label in the middle of levels menu
// by x axis.
const int level_type_combo_x_offset = (levels_menu_.width() -
level_type_combo_.width()) / 2;
level_type_combo_.set_location(
level_type_combo_.location().x + level_type_combo_x_offset,
level_type_combo_.location().y);
const int level_type_label_x_offset = (levels_menu_.width() -
level_type_label_.width()) / 2;
level_type_label_.set_location(
level_type_label_.location().x + level_type_label_x_offset,
level_type_label_.location().y);
//Fourth column: eras & mods menu
ypos = ypos_columntop;
xpos += menu_width + column_border_size;
era_label_.set_location(xpos, ypos);
ypos += era_label_.height() + border_size;
no_era_label_.set_location(xpos, ypos);
eras_menu_.set_max_width(menu_width);
eras_menu_.set_max_height(eras_menu_height);
eras_menu_.set_location(xpos, ypos);
// Menu dimensions are only updated when items are set. So do this now.
int erasel_save = eras_menu_.selection();
eras_menu_.set_items(engine_.extras_menu_item_names(ng::create_engine::ERA));
eras_menu_.move_selection(erasel_save);
ypos += eras_menu_height;
//TODO: use when mods_menu_ would be functional.
mod_label_.set_location(xpos, ypos);
ypos += mod_label_.height() + border_size;
mods_menu_.set_max_width(menu_width);
mods_menu_.set_max_height(mods_menu_height);
mods_menu_.set_location(xpos, ypos);
if (mods_menu_.number_of_items() > 0) {
ypos += mods_menu_.height() + border_size;
}
// Make eras and mods menus the same width for alignment
eras_menu_.set_width(std::max(mods_menu_.width(),eras_menu_.width()));
mods_menu_.set_width(std::max(mods_menu_.width(),eras_menu_.width()));
// OK / Cancel buttons
gui::button* left_button = &launch_game_;
gui::button* right_button = &cancel_game_;
#ifdef OK_BUTTON_ON_RIGHT
std::swap(left_button,right_button);
#endif
// Buttons
right_button->set_location(ca.x + ca.w - right_button->width(),
ca.y + ca.h - right_button->height());
left_button->set_location(right_button->location().x - left_button->width() -
gui::ButtonHPadding, ca.y + ca.h - left_button->height());
if (ca.h < 500) {
load_game_.set_location(left_button->location().x - load_game_.width() -
gui::ButtonHPadding, ca.y + ca.h - load_game_.height());
}
}
} // namespace mp

View file

@ -1,121 +0,0 @@
/*
Copyright (C) 2007 - 2016 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 as published by
the Free Software Foundation; either version 2 of the License, 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 */
#ifndef MULTIPLAYER_CREATE_HPP_INCLUDED
#define MULTIPLAYER_CREATE_HPP_INCLUDED
#include "depcheck.hpp"
#include "create_engine.hpp"
#include "multiplayer_ui.hpp"
#include "widgets/slider.hpp"
#include "widgets/combo.hpp"
#include "widgets/multimenu.hpp"
#include "tooltips.hpp"
namespace mp {
class create : public mp::ui
{
public:
create(CVideo& v, wesnothd_connection* connection, const config& game_config, saved_game& state,
chat& c, config& gamelist);
~create();
const mp_game_settings& get_parameters();
protected:
virtual void layout_children(const SDL_Rect& rect);
virtual void process_event();
virtual void hide_children(bool hide=true);
private:
void init_level_type_changed(size_t index);
void init_level_changed(size_t index);
void synchronize_selections();
void draw_level_image();
void set_description(const std::string& description);
void update_mod_menu();
std::string select_campaign_difficulty();
tooltips::manager tooltip_manager_;
int era_selection_;
int mod_selection_;
int level_selection_;
gui::menu eras_menu_;
gui::menu levels_menu_;
gui::multimenu mods_menu_;
gui::label filter_name_label_;
gui::label filter_num_players_label_;
gui::label map_generator_label_;
gui::label era_label_;
gui::label no_era_label_;
gui::label mod_label_;
gui::label map_size_label_;
gui::label num_players_label_;
gui::label level_type_label_;
gui::button launch_game_;
gui::button cancel_game_;
gui::button regenerate_map_;
gui::button generator_settings_;
gui::button load_game_;
gui::combo level_type_combo_;
gui::slider filter_num_players_slider_;
gui::textbox description_;
gui::textbox filter_name_;
std::unique_ptr<surface_restorer> image_restorer_;
SDL_Rect image_rect_;
std::vector<ng::level::TYPE> available_level_types_;
ng::create_engine engine_;
struct process_event_data {
bool create, load, quit;
boost::optional<std::string> filename;
process_event_data()
: create(false), load(false), quit(false), filename()
{}
process_event_data(bool c, bool l, bool q)
: create(c), load(l), quit(q), filename()
{}
process_event_data(const std::string & fname)
: create(false), load(true), quit(false), filename(fname)
{}
};
void process_event_impl(const process_event_data &);
bool plugin_event_helper(const process_event_data &);
void select_level_type_helper(const std::string & str);
};
} // end namespace mp
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,267 +0,0 @@
/*
Copyright (C) 2007 - 2016 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 as published by
the Free Software Foundation; either version 2 of the License, 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 multiplayer_lobby.hpp */
#ifndef MULTIPLAYER_LOBBY_HPP_INCLUDED
#define MULTIPLAYER_LOBBY_HPP_INCLUDED
#include "multiplayer_ui.hpp"
#include "game_preferences.hpp"
#include "image.hpp"
#include "serialization/string_utils.hpp"
#include <boost/dynamic_bitset.hpp>
class config;
class video;
class CVideo;
/**
* This module controls the multiplayer lobby.
*
* A section on the server which allows players to chat, create games, and join
* games.
*/
namespace mp {
enum ADDON_REQ { SATISFIED, NEED_DOWNLOAD, CANNOT_SATISFY };
struct required_addon {
std::string addon_id;
ADDON_REQ outcome;
std::string message;
};
class gamebrowser : public gui::menu {
public:
struct game_item {
game_item() :
mini_map(),
id(),
map_data(),
name(),
map_info(),
map_info_size(),
era_and_mod_info(),
gold(),
xp(),
vision(),
status(),
time_limit(),
vacant_slots(0),
current_turn(0),
reloaded(false),
started(false),
fog(false),
shroud(false),
observers(false),
shuffle_sides(false),
use_map_settings(false),
verified(false),
password_required(false),
have_scenario(false),
have_era(false),
have_all_mods(false),
addons(),
addons_outcome(SATISFIED)
{
}
surface mini_map;
std::string id;
std::string map_data;
std::string name;
std::string map_info;
std::string map_info_size;
std::string era_and_mod_info;
std::string gold;
std::string xp;
std::string vision;
std::string status;
std::string time_limit;
size_t vacant_slots;
unsigned int current_turn;
bool reloaded;
bool started;
bool fog;
bool shroud;
bool observers;
bool registered_users_only;
bool shuffle_sides;
bool use_map_settings;
bool verified;
bool password_required;
bool have_scenario;
bool have_era;
bool have_all_mods;
std::vector<required_addon> addons;
ADDON_REQ addons_outcome;
};
gamebrowser(CVideo& video, const config &map_hashes);
void scroll(unsigned int pos);
void handle_event(const SDL_Event& event);
void set_inner_location(const SDL_Rect& rect);
void set_item_height(unsigned int height);
void populate_game_item(game_item &, const config &, const config &, const std::vector<std::string> &);
void populate_game_item_campaign_or_scenario_info(game_item &, const config &, const config &, bool &);
void populate_game_item_map_info(game_item &, const config &, const config &, bool &);
void populate_game_item_era_info(game_item &, const config &, const config &, bool &);
void populate_game_item_mod_info(game_item &, const config &, const config &, bool &);
void populate_game_item_addons_installed(game_item &, const config &, const std::vector<std::string> &);
void set_game_items(const config& cfg, const config& game_config, const std::vector<std::string> & installed_addons);
void draw();
void draw_contents();
void draw_row(const size_t row_index, const SDL_Rect& rect, ROW_TYPE type);
SDL_Rect get_item_rect(size_t index) const;
bool empty() const { return games_.empty(); }
bool selection_is_joinable() const
{ return selection_in_joinable_state() && (!selection_needs_content() || selection_needs_addons()); }
bool selection_is_observable() const
{ return selection_in_observable_state() && (!selection_needs_content() || selection_needs_addons()); }
bool selection_needs_content() const
{ return empty() ? false : !games_[selected_].have_era ||
!games_[selected_].have_all_mods ||
!games_[selected_].have_scenario; }
bool selection_needs_addons() const
{ return empty() ? false : (games_[selected_].addons_outcome != SATISFIED); }
bool selection_in_joinable_state() const
{ return empty() ? false : (games_[selected_].vacant_slots > 0 && !games_[selected_].started); }
// Moderators may observe any game.
bool selection_in_observable_state() const
{ return empty() ? false : (games_[selected_].observers || preferences::is_authenticated()); }
ADDON_REQ selection_addon_outcome() const
{ return empty() ? SATISFIED : games_[selected_].addons_outcome; }
const std::vector<required_addon> * selection_addon_requirements() const
{ return empty() ? nullptr : &games_[selected_].addons; }
bool selected() const { return double_clicked_ && !empty(); }
void reset_selection() { double_clicked_ = false; }
int selection() const { return selected_; }
game_item selected_game() { return games_[selected_]; }
void select_game(const std::string& id);
bool game_matches_filter(const game_item& i, const config& cfg);
protected:
unsigned int row_height() const { return item_height_ + (2 * style_->get_thickness()); }
private:
image::locator gold_icon_locator_;
image::locator xp_icon_locator_;
image::locator map_size_icon_locator_;
image::locator vision_icon_locator_;
image::locator time_limit_icon_locator_;
image::locator observer_icon_locator_;
image::locator no_observer_icon_locator_;
image::locator shuffle_sides_icon_locator_;
const config &map_hashes_;
unsigned int item_height_;
int margin_;
int minimap_size_;
int h_padding_;
int h_padding_image_to_text_;
int header_height_;
size_t selected_;
std::pair<size_t, size_t> visible_range_;
std::vector<game_item> games_;
std::vector<size_t> redraw_items_;
std::vector<int> widths_;
bool double_clicked_;
bool ignore_next_doubleclick_;
bool last_was_doubleclick_;
};
class lobby : public ui
{
public:
lobby(CVideo& v, wesnothd_connection* connection, const config& cfg, chat& c, config& gamelist, const std::vector<std::string> & installed_addons);
virtual void process_event();
int current_turn;
protected:
virtual void hide_children(bool hide=true);
virtual void layout_children(const SDL_Rect& rect);
virtual void process_network_data(const config& data);
virtual void gamelist_updated(bool silent=true);
private:
class lobby_sorter : public gui::menu::basic_sorter
{
const config& cfg_;
bool column_sortable(int column) const;
bool less(int column, const gui::menu::item& row1, const gui::menu::item& row2) const;
enum { MAP_COLUMN = 0, STATUS_COLUMN = 2};
public:
lobby_sorter(const config& cfg);
};
boost::dynamic_bitset<> game_vacant_slots_;
boost::dynamic_bitset<> game_observers_;
gui::button observe_game_;
gui::button join_game_;
gui::button create_game_;
gui::combo replay_options_;
gui::button game_preferences_;
gui::button quit_game_;
gui::button apply_filter_;
gui::button invert_filter_;
gui::button vacant_slots_;
gui::button friends_in_game_;
gui::label filter_label_;
gui::textbox filter_text_;
int last_selected_game_;
lobby_sorter sorter_;
gamebrowser games_menu_;
std::map<std::string,std::string> minimaps_;
std::string search_string_;
const std::vector<std::string> & installed_addons_;
struct process_event_data {
bool join;
bool observe;
bool create;
bool quit;
process_event_data()
: join(false), observe(false), create(false), quit(false)
{}
process_event_data(bool j, bool o, bool c, bool q)
: join(j), observe(o), create(c), quit(q)
{}
};
// This is added to make it easier for plugins to trigger events, and to separate some of the "controller" from the internal logic
void process_event_impl(const process_event_data &);
bool plugin_event_helper(const process_event_data &);
};
} // end namespace mp
#endif

View file

@ -1,800 +0,0 @@
/*
Copyright (C) 2005 - 2016 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 as published by
the Free Software Foundation; either version 2 of the License, 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 "construct_dialog.hpp"
#include "video.hpp"
#include "font/sdl_ttf.hpp"
#include "font/standard_colors.hpp"
#include "formatter.hpp"
#include "game_preferences.hpp"
#include "gettext.hpp"
#include "gui/dialogs/multiplayer/mp_cmd_wrapper.hpp"
#include "gui/dialogs/loadscreen.hpp"
#include "lobby_preferences.hpp"
#include "log.hpp"
#include "font/marked-up_text.hpp"
#include "menu_events.hpp"
#include "game_initialization/multiplayer.hpp"
#include "game_initialization/multiplayer_ui.hpp"
#include "game_initialization/multiplayer_lobby.hpp" //needed for dynamic cast when implementing the lobby_sounds preference
#include "mp_ui_alerts.hpp"
#include "wml_separators.hpp"
#include "formula/string_utils.hpp"
#include "scripting/plugins/context.hpp"
#include "scripting/plugins/manager.hpp"
#include "team.hpp"
#include "sdl/utils.hpp"
#include "sdl/rect.hpp"
#include "wesnothd_connection.hpp"
static lg::log_domain log_config("config");
#define ERR_CF LOG_STREAM(err, log_config)
static lg::log_domain log_network("network");
#define LOG_NW LOG_STREAM(info, log_network)
#define ERR_NW LOG_STREAM(err, log_network)
static lg::log_domain log_mp("mp/main");
#define DBG_MP LOG_STREAM(debug, log_mp)
namespace {
/** The maximum number of messages in the chat history. */
const size_t max_messages = 256;
class user_menu_style : public gui::menu::imgsel_style {
public:
user_menu_style() : gui::menu::imgsel_style("dialogs/selection", false,
0x000000, 0x4a4440, 0x999999,
0.0, 0.2, 0.2),
item_size_(sdl::empty_rect)
{}
virtual void init();
virtual SDL_Rect item_size(const std::string& /*item*/) const { return item_size_; }
void set_width(const int width) { item_size_.w = width; }
private:
SDL_Rect item_size_;
};
void user_menu_style::init()
{
imgsel_style::init();
item_size_.h = font::get_max_height(font_size_);
scale_images(-1, item_size_.h);
item_size_.h += 2 * thickness_;
}
user_menu_style umenu_style;
} // anon namespace
namespace mp {
std::string get_color_string(int id)
{
std::string prefix = team::get_side_highlight(id);
std::map<std::string, t_string>::iterator name = game_config::team_rgb_name.find(std::to_string(id + 1));
if(name != game_config::team_rgb_name.end()){
return prefix + name->second;
}else{
return prefix + _("Invalid Color");
}
}
std::string get_color_string(const std::string& id)
{
std::map<std::string, color_range>::iterator i_color = game_config::team_rgb_range.find(id);
std::map<std::string, t_string>::iterator i_name = game_config::team_rgb_name.find(id);
bool has_color = i_color != game_config::team_rgb_range.end();
bool has_name= i_name != game_config::team_rgb_name.end();
return rgb2highlight(has_color ? i_color->second.mid() : 0x00FF0000) + (has_name ? std::string(i_name->second) : _("Invalid Color"));
}
chat::chat() :
message_history_(),
last_update_()
{
}
void chat::add_message(const time_t& time, const std::string& user,
const std::string& message)
{
message_history_.push_back(msg(time, user, message));
while (message_history_.size() > max_messages) {
message_history_.pop_front();
if (last_update_ > 0)
last_update_--;
}
}
void chat::init_textbox(gui::textbox& textbox)
{
for(msg_hist::const_iterator itor = message_history_.begin();
itor != message_history_.end(); ++itor) {
textbox.append_text(format_message(*itor), true, color_message(*itor));
}
last_update_ = message_history_.size();
}
void chat::update_textbox(gui::textbox& textbox)
{
//DBG_MP << "update_textbox...\n";
for(msg_hist::const_iterator itor = message_history_.begin() + last_update_;
itor != message_history_.end(); ++itor) {
textbox.append_text(format_message(*itor), true, color_message(*itor));
}
//DBG_MP << "update_textbox end\n";
last_update_ = message_history_.size();
}
void chat::clear_history()
{
message_history_.clear();
last_update_ = 0;
}
std::string chat::format_message(const msg& message)
{
std::string msg_text = message.message;
if(message.user == "server"
|| message.user.substr(0,29) == "whisper: server message from ") {
std::string::const_iterator after_markup =
font::parse_markup(message.message.begin(), message.message.end(), nullptr, nullptr, nullptr);
msg_text = std::string(after_markup,message.message.end());
}
if(message.message.substr(0,3) == "/me") {
return preferences::get_chat_timestamp(message.time) + "<" + message.user
+ msg_text.substr(3) + ">\n";
} else {
return preferences::get_chat_timestamp(message.time) + "<" + message.user
+ "> " + msg_text + "\n";
}
}
SDL_Color chat::color_message(const msg& message) {
SDL_Color c = font::NORMAL_COLOR;
// Normal users are not allowed to color their messages
if(message.user == "server"
|| message.user.substr(0,29) == "whisper: server message from ") {
font::parse_markup(message.message.begin(), message.message.end(), nullptr, &c, nullptr);
// Highlight private messages too
} else if(message.user.substr(0,8) == "whisper:") {
c = font::LABEL_COLOR;
}
return c;
}
ui::ui(CVideo& video, wesnothd_connection* connection, const std::string& title, const config& cfg, chat& c, config& gamelist) :
gui::widget(video),
video_(video),
wesnothd_connection_(connection),
initialized_(false),
gamelist_initialized_(false),
game_config_(cfg),
chat_(c),
gamelist_(gamelist),
title_(video_, title, font::SIZE_LARGE, font::TITLE_COLOR),
entry_textbox_(video_, 100),
chat_textbox_(video_, 100, "", false),
users_menu_(video_, std::vector<std::string>(), false, -1, -1, nullptr, &umenu_style),
user_list_(),
selected_game_(""),
selected_user_(""),
selected_user_changed_(false),
result_(CONTINUE),
gamelist_refresh_(false),
lobby_clock_(0),
whisper_warnings_(),
plugins_context_(nullptr)
{
const SDL_Rect area = sdl::create_rect(0
, 0
, video.getx()
, video.gety());
users_menu_.set_numeric_keypress_selection(false);
set_location(area);
}
void ui::process_network()
{
config data;
if(receive_from_server(data)) {
process_network_data(data);
}
//apply diffs at a set interval
if(gamelist_refresh_ && SDL_GetTicks() - lobby_clock_ > game_config::lobby_refresh)
{
const cursor::setter cursor_setter(cursor::WAIT);
gamelist_updated(false);
gamelist_refresh_ = false;
lobby_clock_ = SDL_GetTicks();
}
}
ui::result ui::get_result()
{
return result_;
}
ui::result ui::set_result(ui::result res)
{
result_ = res;
return res;
}
const int ui::xscale_base = 1024;
const int ui::yscale_base = 768;
int ui::xscale(int x) const
{
return (x * width())/ui::xscale_base;
}
int ui::yscale(int y) const
{
return (y * height())/ui::yscale_base;
}
SDL_Rect ui::client_area() const
{
SDL_Rect res;
res.x = xscale(10) + 10;
res.y = yscale(38) + 10;
res.w = xscale(828) > 12 ? xscale(828) - 12 : 0;
res.h = yscale(520) > 12 ? yscale(520) - 12 : 0;
return res;
}
const config& ui::game_config() const
{
return game_config_;
}
void ui::draw_contents()
{
hide_children();
surface background(image::get_image("misc/lobby.png"));
background = scale_surface(background, video().getx(), video().gety());
if(background == nullptr)
return;
sdl_blit(background, nullptr, video().getSurface(), nullptr);
hide_children(false);
}
void ui::set_location(const SDL_Rect& rect)
{
hide_children();
widget::set_location(rect);
layout_children(rect);
if(!initialized_) {
chat_textbox_.set_wrap(true);
chat_.init_textbox(chat_textbox_);
chat_textbox_.set_edit_target(&entry_textbox_);
initialized_ = true;
}
hide_children(false);
}
void ui::process_event()
{
}
void ui::handle_event(const SDL_Event& event)
{
if (gui2::dialogs::loading_screen::displaying()) {
return;
}
gui::widget::handle_event(event);
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) {
SDL_Rect new_location;
new_location.x = 0;
new_location.y = 0;
new_location.w = event.window.data1;
new_location.h = event.window.data2;
set_location(new_location);
}
if(event.type == SDL_KEYDOWN) {
handle_key_event(event.key);
}
if(users_menu_.double_clicked()) {
std::string usr_text = user_list_[users_menu_.selection()];
Uint32 show_time = SDL_GetTicks();
// Hack: for some reason the help string stays visible for ever
/** @todo find out why the help string stays visible and fix it */
video().clear_all_help_strings();
gui2::dialogs::mp_cmd_wrapper dlg(_("Selected user: ") + usr_text);
dlg.show(video());
std::stringstream msg;
switch(dlg.get_retval()) {
case -1:
if(!dlg.message().empty()) msg << "/msg " << usr_text << ' ' << dlg.message();
break;
case 1:
msg << "/friend " << usr_text;
break;
case 2:
msg << "/ignore " << usr_text;
break;
case 3:
msg << "/remove " << usr_text;
break;
case 4:
msg << "/query status " << usr_text;
break;
case 5:
msg << "/query kick " << usr_text;
if(!dlg.reason().empty()) msg << ' ' << dlg.reason();
break;
case 6:
msg << "/query kban " << usr_text;
if(!dlg.time().empty()) msg << ' ' << dlg.time();
if(!dlg.reason().empty()) msg << ' ' << dlg.reason();
}
chat_handler::do_speak(msg.str());
if(show_time + 60000 < SDL_GetTicks()) {
//if the dialog has been open for a long time, refresh the lobby
config request;
request.add_child("refresh_lobby");
send_to_server(request);
}
}
if(users_menu_.selection() > 0 // -1 indicates an invalid selection
&& selected_user_ != user_list_[users_menu_.selection()]) {
selected_user_ = user_list_[users_menu_.selection()];
selected_user_changed_ = true;
}
}
void ui::add_chat_message(const time_t& time, const std::string& speaker, int /*side*/, const std::string& message, events::chat_handler::MESSAGE_TYPE /*type*/)
{
chat_.add_message(time, speaker, message);
chat_.update_textbox(chat_textbox_);
}
void ui::send_chat_message(const std::string& message, bool /*allies_only*/)
{
config data, msg;
msg["message"] = message;
msg["sender"] = preferences::login();
data.add_child("message", msg);
add_chat_message(time(nullptr), preferences::login(),0, message); //local echo
send_to_server(data);
}
void ui::handle_key_event(const SDL_KeyboardEvent& event)
{
//On enter, adds the current chat message to the chat textbox.
if((event.keysym.sym == SDLK_RETURN || event.keysym.sym == SDLK_KP_ENTER) && !entry_textbox_.text().empty()) {
chat_handler::do_speak(entry_textbox_.text());
entry_textbox_.clear();
// nick tab-completion
} else if(event.keysym.sym == SDLK_TAB ) {
std::string text = entry_textbox_.text();
std::vector<std::string> matches = user_list_;
// Exclude own nick from tab-completion.
matches.erase(std::remove(matches.begin(), matches.end(),
preferences::login()), matches.end());
const bool line_start = utils::word_completion(text, matches);
if (matches.empty()) return;
if (matches.size() == 1) {
text.append(line_start ? ": " : " ");
} else {
std::string completion_list = utils::join(matches, " ");
chat_.add_message(time(nullptr), "", completion_list);
chat_.update_textbox(chat_textbox_);
}
entry_textbox_.set_text(text);
}
}
void ui::process_message(const config& msg, const bool whisper) {
const std::string& sender = msg["sender"];
const std::string& message = msg["message"];
std::string room = msg["room"];
if (!preferences::parse_should_show_lobby_join(sender, message)) return;
if (preferences::is_ignored(sender)) return;
// Warn about people trying to whisper a player with the
// whisper_friends_only option enabled.
if (whisper &&
preferences::whisper_friends_only() &&
sender != "server" &&
sender.find(' ') == std::string::npos && // "server message from foo"
!preferences::is_friend(sender))
{
LOG_NW << "Accepting whispers from friends only, ignored whisper from " << sender << '\n';
typedef std::map<std::string, time_t> timetable;
timetable::const_iterator i = whisper_warnings_.find(sender);
time_t last_warning = 0;
const time_t cur_time = time(nullptr);
static const time_t warning_duration = 5 * 60;
if (i != whisper_warnings_.end()) {
last_warning = i->second;
}
//
// Don't warn if it's been less than warning_duration seconds since
// the last warning. Also, make sure the clock isn't running backwards,
// warn anyway if it is.
//
// We don't need to hande the case where preferences change between
// whispers because the lobby instance gets recreated along with the
// table after closing the preferences dialog.
//
if (last_warning && last_warning < cur_time && cur_time - last_warning < warning_duration) {
return;
}
utils::string_map symbols;
symbols["sender"] = sender;
chat_.add_message(cur_time,
"server",
VGETTEXT("$sender is messaging you, and you accept whispers from friends only.", symbols));
chat_.update_textbox(chat_textbox_);
whisper_warnings_[sender] = cur_time;
return;
}
preferences::parse_admin_authentication(sender, message);
bool is_lobby = dynamic_cast<mp::lobby*>(this) != nullptr;
if (whisper || utils::word_match(message, preferences::login())) {
mp_ui_alerts::private_message(is_lobby, sender, message);
} else if (preferences::is_friend(sender)) {
mp_ui_alerts::friend_message(is_lobby, sender, message);
} else if (sender == "server") {
mp_ui_alerts::server_message(is_lobby, sender, message);
} else {
mp_ui_alerts::public_message(is_lobby, sender, message);
}
std::string prefix;
if(whisper) {
utils::string_map symbols;
symbols["sender"] = msg["sender"].str();
prefix = VGETTEXT("whisper: $sender", symbols);
}
else {
prefix = msg["sender"].str();
}
if (!room.empty()) room = room + ": ";
chat_.add_message(time(nullptr), room + prefix, msg["message"]);
chat_.update_textbox(chat_textbox_);
config temp = msg;
temp["whisper"] = whisper;
plugins_manager::get()->notify_event("chat", temp); //notify plugins of the network message
}
void ui::process_network_data(const config& data)
{
if (const config &error = data.child("error")) {
throw wesnothd_error(error["message"]);
} else if (const config &message = data.child("message")) {
process_message(message);
} else if (const config &whisper = data.child("whisper")) {
process_message(whisper, true);
} else if(data.child("gamelist")) {
const cursor::setter cursor_setter(cursor::WAIT);
gamelist_initialized_ = true;
gamelist_ = data;
gamelist_updated(false);
gamelist_refresh_ = false;
lobby_clock_ = SDL_GetTicks();
} else if (const config &gamelist_diff = data.child("gamelist_diff")) {
if(gamelist_initialized_) {
try {
gamelist_.apply_diff(gamelist_diff);
} catch(config::error& e) {
ERR_CF << "Error while applying the gamelist diff: '"
<< e.message << "' Getting a new gamelist.\n";
send_to_server(config("refresh_lobby"));
}
gamelist_refresh_ = true;
}
} else if (const config &room_join = data.child("room_join")) {
if (room_join["player"] == preferences::login()) {
chat_.add_message(time(nullptr), "server",
"You have joined the room '" + room_join["room"].str() + "'");
} else {
chat_.add_message(time(nullptr), "server",
room_join["player"].str() + " has joined the room '" + room_join["room"].str() + "'");
}
chat_.update_textbox(chat_textbox_);
} else if (const config &room_part = data.child("room_part")) {
if (room_part["player"] == preferences::login()) {
chat_.add_message(time(nullptr), "server",
"You have left the room '" + room_part["room"].str() + "'");
} else {
chat_.add_message(time(nullptr), "server",
room_part["player"].str() + " has left the room '" + room_part["room"].str() + "'");
}
chat_.update_textbox(chat_textbox_);
} else if (const config &room_query_response = data.child("room_query_response")) {
if (const config &ms = room_query_response.child("members")) {
std::stringstream ss;
ss << "Room " << room_query_response["room"].str() << " members: ";
for (const config& m : ms.child_range("member")) {
ss << m["name"] << " ";
}
chat_.add_message(time(nullptr), "server", ss.str());
chat_.update_textbox(chat_textbox_);
}
if (const config &rooms = room_query_response.child("rooms")) {
std::stringstream ss;
ss << "Rooms: ";
for (const config& room : rooms.child_range("room")) {
ss << room["name"].str() << "(" << room["size"].str() << ") ";
}
chat_.add_message(time(nullptr), "server", ss.str());
chat_.update_textbox(chat_textbox_);
}
}
}
void ui::hide_children(bool hide)
{
title_.hide(hide);
chat_textbox_.hide(hide);
entry_textbox_.hide(hide);
users_menu_.hide(hide);
}
void ui::layout_children(const SDL_Rect& /*rect*/)
{
title_.set_location(xscale(12) + 8, yscale(38) + 8);
umenu_style.set_width(xscale(159));
users_menu_.set_width(xscale(159));
users_menu_.set_max_width(xscale(159));
users_menu_.set_location(xscale(856), yscale(42));
users_menu_.set_height(yscale(715));
users_menu_.set_max_height(yscale(715));
chat_textbox_.set_location(xscale(11) + 4, yscale(573) + 4);
chat_textbox_.set_measurements(xscale(833) - 8, yscale(143) - 8);
entry_textbox_.set_location(xscale(11) + 4, yscale(732));
entry_textbox_.set_width(xscale(833) - 8);
}
bool ui::user_info::operator> (const user_info& b) const
{
//FIXME: to cmpare names, use translation::compare from gettext.hpp
user_info const& a = *this;
// ME always on top
if (a.relation == ME) {
return true;
}
if (b.relation == ME) {
return false;
}
// friends next, sorted by location
if ((a.relation == FRIEND) && (b.relation == FRIEND)) {
if (a.state != b.state) {
return a.state < b.state;
}
return a.name < b.name;
}
if (a.relation == FRIEND) {
return true;
}
if (b.relation == FRIEND) {
return false;
}
// players in the selected game next, sorted by relation (friends/neutral/ignored)
if ((a.state == SEL_GAME) && (b.state == SEL_GAME)) {
if (a.relation != b.relation) {
return a.relation < b.relation;
}
return a.name < b.name;
}
if (a.state == SEL_GAME) {
return true;
}
if (b.state == SEL_GAME) {
return false;
}
// all others grouped by relation
if (a.relation != b.relation) {
return a.relation < b.relation;
}
if (a.state != b.state) {
return a.state < b.state;
}
return a.name < b.name;
}
void ui::gamelist_updated(bool silent)
{
std::list<user_info> u_list;
for (const config &user : gamelist_.child_range("user"))
{
user_info u_elem;
u_elem.name = user["name"].str();
u_elem.state = user["available"].to_bool(true) ? LOBBY : GAME;
u_elem.registered = user["registered"].to_bool();
u_elem.game_id = user["game_id"].str();
u_elem.location = user["location"].str();
if (!u_elem.game_id.empty() && u_elem.game_id == selected_game_) {
u_elem.state = SEL_GAME;
}
if (u_elem.name == preferences::login()) {
u_elem.relation = ME;
} else if (preferences::is_ignored(u_elem.name)) {
u_elem.relation = IGNORED;
} else if (preferences::is_friend(u_elem.name)) {
u_elem.relation = FRIEND;
} else {
u_elem.relation = NEUTRAL;
}
u_list.push_back(u_elem);
}
if (preferences::sort_list()) {
u_list.sort(std::greater<user_info>());
}
// can't use the bold tag here until the menu code
// calculates a correct ellipsis for it
const std::string lobby_color_tag = "";
const std::string ingame_color_tag = "#";
const std::string selgame_color_tag = "<0,191,255>";
// for now I just disregard the above till I know something better,
// it works for me anyways
const std::string registered_user_tag = "~";
const std::string imgpre = IMAGE_PREFIX + std::string("misc/status-");
std::vector<std::string> user_strings;
std::vector<std::string> menu_strings;
std::list<user_info>::const_iterator u_itor = u_list.begin();
while (u_itor != u_list.end()) {
const std::string name_str = u_itor->name +
((u_itor->state == LOBBY) ? "" : " (" + u_itor->location + ")");
std::string img_str = "";
std::string color_str = "";
std::string reg_str = "";
switch (u_itor->state) {
case LOBBY: color_str = lobby_color_tag; break;
case GAME: color_str = ingame_color_tag; break;
case SEL_GAME: color_str = selgame_color_tag; break;
}
if (preferences::iconize_list()) {
switch (u_itor->relation) {
case NEUTRAL: img_str = imgpre + "neutral.png" + IMG_TEXT_SEPARATOR; break;
case IGNORED: img_str = imgpre + "ignore.png" + IMG_TEXT_SEPARATOR; break;
case FRIEND: img_str = imgpre + "friend.png" + IMG_TEXT_SEPARATOR; break;
case ME: img_str = imgpre + "self.png" + IMG_TEXT_SEPARATOR; break;
}
}
reg_str = u_itor->registered ? registered_user_tag : "";
user_strings.push_back(u_itor->name);
menu_strings.push_back(img_str + reg_str + color_str + name_str + HELP_STRING_SEPARATOR + name_str);
++u_itor;
}
set_user_list(user_strings, silent);
set_user_menu_items(menu_strings);
}
void ui::set_selected_game(const std::string& game_id)
{
// reposition the player list to show the players in the selected game
if (preferences::sort_list() && (selected_game_ != game_id)) {
users_menu_.move_selection(0);
}
selected_game_ = game_id;
}
void ui::set_user_menu_items(const std::vector<std::string>& list)
{
users_menu_.set_items(list,true,true);
// Try to keep selected player
std::vector<std::string>::const_iterator i =
std::find(user_list_.begin(), user_list_.end(), selected_user_);
if(i != user_list_.end()) {
users_menu_.move_selection_keeping_viewport(i - user_list_.begin());
}
}
void ui::set_user_list(const std::vector<std::string>& list, bool silent)
{
if(!silent) {
bool is_lobby = dynamic_cast<mp::lobby*>(this) != nullptr;
if(list.size() < user_list_.size()) {
mp_ui_alerts::player_leaves(is_lobby);
} else if(list.size() > user_list_.size()) {
mp_ui_alerts::player_joins(is_lobby);
}
}
user_list_ = list;
}
std::string ui::get_selected_user_game()
{
const config &u = gamelist_.find_child("user", "name", selected_user_);
if (u) return u["game_id"];
return std::string();
}
void ui::append_to_title(const std::string& text) {
title_.set_text(title_.get_text() + text);
}
const gui::label& ui::title() const
{
return title_;
}
plugins_context * ui::get_plugins_context()
{
return plugins_context_.get();
}
void ui::send_to_server(const config& cfg)
{
if (wesnothd_connection_) {
wesnothd_connection_->send_data(cfg);
}
}
bool ui::receive_from_server(config& cfg)
{
return wesnothd_connection_ && wesnothd_connection_->receive_data(cfg);
}
}// namespace mp

View file

@ -1,265 +0,0 @@
/*
Copyright (C) 2007 - 2016 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 as published by
the Free Software Foundation; either version 2 of the License, 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.
*/
#ifndef MULTIPLAYER_UI_HPP_INCLUDED
#define MULTIPLAYER_UI_HPP_INCLUDED
#include "chat_events.hpp"
#include "floating_label.hpp"
#include "hotkey/command_executor.hpp"
#include "preferences_display.hpp"
#include "scripting/plugins/context.hpp"
#include "widgets/combo.hpp"
#include "widgets/label.hpp"
#include "widgets/menu.hpp"
#include "widgets/textbox.hpp"
#include <deque>
class display;
class game_display;
class config;
class plugins_context;
class wesnothd_connection;
namespace mp {
std::string get_color_string(int id);
std::string get_color_string(const std::string& id);
/** this class memorizes a chat session. */
class chat
{
public:
chat();
void add_message(const time_t& time, const std::string& user,
const std::string& message);
void init_textbox(gui::textbox& textbox);
void update_textbox(gui::textbox& textbox);
void clear_history();
private:
struct msg {
msg(const time_t& time, const std::string& user, const std::string& message)
: time(time), user(user), message(message) {}
time_t time;
std::string user;
std::string message;
};
typedef std::deque<msg> msg_hist;
std::string format_message(const msg& message);
SDL_Color color_message(const msg& message);
msg_hist message_history_;
msg_hist::size_type last_update_;
};
/**
* a base class for the different multiplayer base dialogs: game list, create
* game, wait game, game setup.
*/
class ui : public gui::widget, private events::chat_handler, private font::floating_label_context
{
public:
enum result { CONTINUE, JOIN, OBSERVE, CREATE, LOAD_GAME, PREFERENCES,
PLAY, QUIT };
ui(CVideo& v, wesnothd_connection* connection, const std::string& title,
const config& cfg, chat& c, config& gamelist);
/**
* Asks the multiplayer_ui to pump some data from the network, and then to
* process it. The actual processing will be left to the child classes,
* through process_network_data and process_network_error.
*/
void process_network();
/**
* Returns the result of the current widget. While the result is equal to
* continue, the widget should not be destroyed.
*/
result get_result();
/**
* Hides children, moves them (using layout_children), then shows them.
*
* The methodes hide_children and layout_children are supposed to be
* overridden by subclasses of this class which add new sub-widgets.
*/
void set_location(const SDL_Rect& rect) override;
using widget::set_location;
const std::vector<std::string>& user_list() const { return user_list_; }
void send_to_server(const config& cfg) override;
bool receive_from_server(config& dst);
protected:
int xscale(int x) const;
int yscale(int y) const;
static const int xscale_base;
static const int yscale_base;
SDL_Rect client_area() const;
CVideo& video_;
wesnothd_connection* wesnothd_connection_;
CVideo& video() { return video_; }
/**
* Returns the main game config, as defined by loading the preprocessed WML
* files. Children of this class may need this to obtain, for example, the
* list of available eras.
*/
const config& game_config() const;
virtual void draw_contents() override;
virtual void process_event() override;
virtual void handle_event(const SDL_Event& event) override;
virtual void handle_key_event(const SDL_KeyboardEvent& event);
/** Override chat_handler. */
void add_chat_message(const time_t& time, const std::string& speaker,
int side, const std::string& message,
events::chat_handler::MESSAGE_TYPE type=events::chat_handler::MESSAGE_PRIVATE) override;
void send_chat_message(const std::string& message, bool allies_only=false) override;
/** Process chat messages. */
void process_message(const config& msg, const bool whisper=false);
/**
* Processes any pending network data. Called by the public
* process_network() method. Overridden by subclasses who add more
* behavior for network.
*/
virtual void process_network_data(const config& data);
/**
* Hides or shows all gui::widget children of this widget. Should be
* overridden by subclasses which add their own children.
*/
virtual void hide_children(bool hide=true);
/**
* Lays the children out. This method is to be overridden by the subclasses
* of the mp_ui class; it will be called.
*/
virtual void layout_children(const SDL_Rect& rect);
/** Sets the result of this dialog, to be checked by get_result(). */
result set_result(result res);
/**
* Sets the name of the selected game which is used to highlight the names
* of the players which have joined this game.
*/
void set_selected_game(const std::string& game_name);
/**
* Called each time the gamelist_ variable is updated. May be
* overridden by child classes to add custom gamelist behavior.
*/
virtual void gamelist_updated(bool silent=true);
/** Sets the user list */
void set_user_list(const std::vector<std::string>&, bool silent);
void set_user_menu_items(const std::vector<std::string>& list);
/** Returns the current gamelist */
config& gamelist() { return gamelist_; }
void append_to_title(const std::string& name);
const gui::label& title() const;
std::string get_selected_user_game();
bool selected_user_changed() const { return selected_user_changed_; }
void set_selected_user_changed(const bool& changed) { selected_user_changed_ = changed; }
private:
/**
* Set to true when the widgets are initialized. Allows delayed
* initialization on first positioning.
*/
bool initialized_;
bool gamelist_initialized_;
/**
* The main game configuration, as defined by loading the preprocessed WML
* files. Access using the game_config() method if necessary.
*/
const config& game_config_;
chat& chat_;
config& gamelist_;
gui::label title_;
gui::textbox entry_textbox_;
gui::textbox chat_textbox_;
gui::menu users_menu_;
std::vector<std::string> user_list_;
std::string selected_game_;
std::string selected_user_;
bool selected_user_changed_;
result result_;
bool gamelist_refresh_;
Uint32 lobby_clock_;
std::map<std::string, time_t> whisper_warnings_;
public:
enum user_relation { ME, FRIEND, NEUTRAL, IGNORED };
enum user_state { LOBBY, GAME, SEL_GAME };
private:
struct user_info
{
user_info() :
name(),
game_id(),
location(),
relation(ME),
state(LOBBY),
registered()
{
}
std::string name;
std::string game_id;
std::string location;
user_relation relation;
user_state state;
/** True if this user is registered on the server. */
bool registered;
bool operator> (const user_info& b) const;
};
protected:
std::unique_ptr<plugins_context> plugins_context_;
public:
plugins_context * get_plugins_context();
};
}
#endif

View file

@ -1,537 +0,0 @@
/*
Copyright (C) 2007 - 2016 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 as published by
the Free Software Foundation; either version 2 of the License, 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 "construct_dialog.hpp"
#include "gettext.hpp"
#include "game_config_manager.hpp"
#include "game_preferences.hpp"
#include "gui/dialogs/multiplayer/faction_select.hpp"
#include "gui/dialogs/transient_message.hpp"
#include "gui/dialogs/network_transmission.hpp"
#include "gui/widgets/window.hpp"
#include "image.hpp"
#include "log.hpp"
#include "font/marked-up_text.hpp"
#include "font/standard_colors.hpp"
#include "game_initialization/mp_game_utils.hpp"
#include "game_initialization/multiplayer_wait.hpp"
#include "statistics.hpp"
#include "saved_game.hpp"
#include "mp_ui_alerts.hpp"
#include "scripting/plugins/context.hpp"
#include "sdl/rect.hpp"
#include "units/types.hpp"
#include "wml_exception.hpp"
#include "wml_separators.hpp"
#include "formula/string_utils.hpp"
#include "video.hpp"
#include "utils/functional.hpp"
static lg::log_domain log_network("network");
#define DBG_NW LOG_STREAM(debug, log_network)
#define LOG_NW LOG_STREAM(info, log_network)
static lg::log_domain log_enginerefac("enginerefac");
#define LOG_RG LOG_STREAM(info, log_enginerefac)
#define ERR_RG LOG_STREAM(err, log_enginerefac)
static lg::log_domain log_mp("mp/main");
#define DBG_MP LOG_STREAM(debug, log_mp)
#define ERR_MP LOG_STREAM(err, log_mp)
namespace mp {
wait::wait(CVideo& v, wesnothd_connection* connection, const config& cfg, saved_game& state,
mp::chat& c, config& gamelist, const bool first_scenario) :
ui(v, connection, _("Game Lobby"), cfg, c, gamelist),
cancel_button_(video(), first_scenario ? _("Cancel") : _("Quit")),
start_label_(video(), _("Waiting for game to start..."), font::SIZE_SMALL, font::LOBBY_COLOR),
game_menu_(video(), std::vector<std::string>(), false, -1, -1, nullptr, &gui::menu::bluebg_style),
level_(),
state_(state),
first_scenario_(first_scenario),
stop_updates_(false)
{
game_menu_.set_numeric_keypress_selection(false);
gamelist_updated();
plugins_context_.reset(new plugins_context("Multiplayer Wait"));
//These structure initializers create a lobby::process_data_event
plugins_context_->set_callback("quit", std::bind(&wait::process_event_impl, this, true), false);
plugins_context_->set_callback("chat", [this](const config& cfg) { send_chat_message(cfg["message"], false); }, true);
}
wait::~wait()
{
try {
if (get_result() == QUIT) {
state_ = saved_game();
state_.classification().campaign_type = game_classification::CAMPAIGN_TYPE::MULTIPLAYER;
}
} catch (...) {}
}
void wait::process_event()
{
process_event_impl(cancel_button_.pressed());
}
void wait::process_event_impl(bool quit)
{
if (quit) {
set_result(QUIT);
}
}
void wait::join_game(bool observe)
{
const bool download_res = download_level_data();
if (!download_res) {
DBG_MP << "mp wait: could not download level data, quitting...";
set_result(QUIT);
return;
} else if (level_["started"].to_bool()) {
set_result(PLAY);
return;
}
if (first_scenario_) {
state_ = saved_game();
state_.classification() = game_classification(level_);
if(state_.classification().campaign_type != game_classification::CAMPAIGN_TYPE::MULTIPLAYER) {
ERR_MP << "Mp wait recieved a game that is not a multiplayer game\n";
}
// Make sure that we have the same config as host, if possible.
game_config_manager::get()->load_game_config_for_game(state_.classification());
}
// Add the map name to the title.
append_to_title(": " + get_scenario()["name"].t_str());
game_config::add_color_info(get_scenario());
if (!observe) {
//search for an appropriate vacant slot. If a description is set
//(i.e. we're loading from a saved game), then prefer to get the side
//with the same description as our login. Otherwise just choose the first
//available side.
const config *side_choice = nullptr;
int side_num = -1, nb_sides = 0;
for (const config &sd : get_scenario().child_range("side"))
{
DBG_MP << "*** side " << nb_sides << "***\n" << sd.debug() << "***\n";
if (sd["controller"] == "reserved" && sd["current_player"] == preferences::login())
{
side_choice = &sd;
side_num = nb_sides;
break;
}
if (sd["controller"] == "human" && sd["player_id"].empty())
{
if (!side_choice) { // found the first empty side
side_choice = &sd;
side_num = nb_sides;
}
if (sd["current_player"] == preferences::login()) {
side_choice = &sd;
side_num = nb_sides;
break; // found the preferred one
}
}
if (sd["player_id"] == preferences::login())
{
//We already own a side in this game.
generate_menu();
return;
}
++nb_sides;
}
if (!side_choice) {
DBG_MP << "could not find a side, all " << get_scenario().child_count("side") << " sides were unsuitable\n";
set_result(QUIT);
return;
}
bool allow_changes = (*side_choice)["allow_changes"].to_bool(true);
//if the client is allowed to choose their team, instead of having
//it set by the server, do that here.
if(allow_changes) {
events::event_context context;
const config &era = level_.child("era");
/** @todo Check whether we have the era. If we don't inform the user. */
if (!era)
throw config::error(_("No era information found."));
config::const_child_itors possible_sides = era.child_range("multiplayer_side");
if (possible_sides.empty()) {
set_result(QUIT);
throw config::error(_("No multiplayer sides found"));
}
const std::string color = (*side_choice)["color"].str();
std::vector<const config*> era_factions;
for (const config &side : possible_sides) {
era_factions.push_back(&side);
}
const bool is_mp = state_.classification().is_normal_mp_game();
const bool lock_settings =
get_scenario()["force_lock_settings"].to_bool(!is_mp);
const bool use_map_settings =
level_.child("multiplayer")["mp_use_map_settings"].to_bool();
const bool saved_game =
level_.child("multiplayer")["savegame"].to_bool();
ng::flg_manager flg(era_factions, *side_choice, lock_settings, use_map_settings,
saved_game);
std::vector<std::string> choices;
for (const config *s : flg.choosable_factions())
{
const config &side = *s;
const std::string &name = side["name"];
const std::string &icon = side["image"];
if (!icon.empty()) {
std::string rgb = side["flag_rgb"];
if (rgb.empty())
rgb = "magenta";
choices.push_back(IMAGE_PREFIX + icon + "~RC(" + rgb + ">" +
color + ")" + COLUMN_SEPARATOR + name);
} else {
choices.push_back(name);
}
}
gui2::dialogs::faction_select dlg(flg, color, side_num + 1);
dlg.show(video());
if(dlg.get_retval() != gui2::window::OK) {
set_result(QUIT);
return;
}
config faction;
config& change = faction.add_child("change_faction");
change["change_faction"] = true;
change["name"] = preferences::login();
change["faction"] = flg.current_faction()["id"];
change["leader"] = flg.current_leader();
change["gender"] = flg.current_gender();
send_to_server(faction);
}
}
generate_menu();
}
void wait::start_game()
{
if (const config &stats = level_.child("statistics")) {
statistics::fresh_stats();
statistics::read_stats(stats);
}
level_to_gamestate(level_, state_);
LOG_NW << "starting game\n";
mp_ui_alerts::game_has_begun();
}
void wait::layout_children(const SDL_Rect& rect)
{
ui::layout_children(rect);
const SDL_Rect ca = client_area();
int y = ca.y + ca.h - cancel_button_.height();
game_menu_.set_location(ca.x, ca.y + title().height());
game_menu_.set_measurements(ca.w, y - ca.y - title().height()
- gui::ButtonVPadding);
game_menu_.set_max_width(ca.w);
game_menu_.set_max_height(y - ca.y - title().height() - gui::ButtonVPadding);
cancel_button_.set_location(ca.x + ca.w - cancel_button_.width(), y);
start_label_.set_location(ca.x, y + 4);
}
void wait::hide_children(bool hide)
{
ui::hide_children(hide);
cancel_button_.hide(hide);
game_menu_.hide(hide);
}
void wait::process_network_data(const config& data)
{
ui::process_network_data(data);
if(!data["message"].empty()) {
gui2::show_transient_message(video()
, _("Response")
, data["message"]);
}
if (data["failed"].to_bool()) {
set_result(QUIT);
return;
} else if(data.child("stop_updates")) {
stop_updates_ = true;
} else if(data.child("start_game")) {
LOG_NW << "received start_game message\n";
set_result(PLAY);
return;
} else if(data.child("leave_game")) {
set_result(QUIT);
return;
} else if (const config &c = data.child("scenario_diff")) {
LOG_NW << "received diff for scenario... applying...\n";
/** @todo We should catch config::error and then leave the game. */
level_.apply_diff(c);
generate_menu();
} else if(const config &change = data.child("change_controller")) {
LOG_NW << "received change controller" << std::endl;
LOG_RG << "multiplayer_wait: [change_controller]" << std::endl;
LOG_RG << data.debug() << std::endl;
//const int side = std::stoi(change["side"]);
if (config & sidetochange = get_scenario().find_child("side", "side", change["side"])) {
LOG_RG << "found side : " << sidetochange.debug() << std::endl;
sidetochange.merge_with(change);
LOG_RG << "changed to : " << sidetochange.debug() << std::endl;
} else {
LOG_RG << "change_controller didn't find any side!" << std::endl;
}
} else if(data.has_child("scenario") || data.has_child("snapshot") || data.child("next_scenario")) {
level_ = first_scenario_ ? data : data.child("next_scenario");
LOG_NW << "got some sides. Current number of sides = "
<< get_scenario().child_count("side") << ','
<< data.child_count("side") << '\n';
generate_menu();
}
}
static std::string generate_user_description(const config& side)
{
//allow the host to overwrite it, this is needed because only the host knows the ai_algorithm.
if(const config::attribute_value* desc = side.get("user_description")) {
return desc->str();
}
std::string controller_type = side["controller"].str();
std::string reservation = side["reserved_for"].str();
std::string owner = side["player_id"].str();
if(controller_type == "ai") {
return _("Computer Player");
}
else if (controller_type == "null") {
return _("(Empty slot)");
}
else if (controller_type == "reserved") {
utils::string_map symbols;
symbols["playername"] = reservation;
return vgettext("(Reserved for $playername)",symbols);
}
else if(owner.empty()) {
return _("(Vacant slot)");
}
else if (controller_type == "human" || controller_type == "network") {
return owner;
}
else {
ERR_RG << "Found unknown controller type:" << controller_type << std::endl;
return _("(empty)");
}
}
void wait::generate_menu()
{
if (stop_updates_)
return;
std::vector<std::string> details;
std::set<std::string> playerlist;
for (const config &sd : get_scenario().child_range("side"))
{
if (!sd["allow_player"].to_bool(true)) {
continue;
}
std::string description = generate_user_description(sd);
t_string side_name = sd["faction_name"];
std::string leader_type = sd["type"];
std::string gender_id = sd["gender"];
// Hack: if there is a unit which can recruit, use it as a
// leader. Necessary to display leader information when loading
// saves.
for (const config &side_unit : sd.child_range("unit"))
{
if (side_unit["canrecruit"].to_bool()) {
leader_type = side_unit["type"].str();
break;
}
}
if(!sd["player_id"].empty())
playerlist.insert(sd["player_id"]);
std::string leader_name;
std::string leader_image;
const unit_type *ut = unit_types.find(leader_type);
if (ut) {
const unit_type &utg = ut->get_gender_unit_type(gender_id);
leader_name = utg.type_name();
std::string RCcolor = sd["color"];
if (RCcolor.empty())
RCcolor = sd["side"].str();
leader_image = utg.image() + std::string("~RC(") + utg.flag_rgb() + ">" + RCcolor + ")";
} else {
leader_image = ng::random_enemy_picture;
}
if (!leader_image.empty()) {
// Dumps the "image" part of the faction name, if any,
// to replace it by a picture of the actual leader
if(side_name.str()[0] == font::IMAGE) {
std::string::size_type p =
side_name.str().find_first_of(COLUMN_SEPARATOR);
if(p != std::string::npos && p < side_name.size()) {
side_name = IMAGE_PREFIX + leader_image + COLUMN_SEPARATOR + side_name.str().substr(p+1);
}
} else {
// no image prefix, just add the leader image
// (assuming that there is also no COLUMN_SEPARATOR)
side_name = IMAGE_PREFIX + leader_image + COLUMN_SEPARATOR + side_name.str();
}
}
std::stringstream str;
str << sd["side"] << ". " << COLUMN_SEPARATOR;
str << description << COLUMN_SEPARATOR << side_name << COLUMN_SEPARATOR;
// Mark parentheses translatable for languages like Japanese
if(!leader_name.empty())
str << _("(") << leader_name << _(")");
str << COLUMN_SEPARATOR;
// Don't show gold for saved games
if (sd["allow_changes"].to_bool())
str << sd["gold"] << ' ' << _n("multiplayer_starting_gold^Gold", "multiplayer_starting_gold^Gold", sd["gold"].to_int()) << COLUMN_SEPARATOR;
int income_amt = sd["income"];
if(income_amt != 0){
str << _("(") << _("Income") << ' ';
if(income_amt > 0)
str << _("+");
str << sd["income"] << _(")");
}
str << COLUMN_SEPARATOR << t_string::from_serialized(sd["user_team_name"].str());
str << COLUMN_SEPARATOR << get_color_string(sd["color"].str());
details.push_back(str.str());
}
game_menu_.set_items(details);
// Uses the actual connected player list if we do not have any
// "gamelist" user data
if (!gamelist().child("user")) {
set_user_list(std::vector<std::string>(playerlist.begin(), playerlist.end()), true);
}
}
bool wait::download_level_data()
{
assert(wesnothd_connection_);
DBG_MP << "download_level_data()\n";
if (!first_scenario_) {
// Ask for the next scenario data.
send_to_server(config("load_next_scenario"));
}
bool has_scenario_and_controllers = false;
while (!has_scenario_and_controllers) {
config revc;
bool data_res = gui2::dialogs::network_transmission::wesnothd_receive_dialog(
video(), "download level data", revc, *wesnothd_connection_);
if (!data_res) {
DBG_MP << "download_level_data bad results\n";
return false;
}
check_response(data_res, revc);
if (revc.child("leave_game")) {
return false;
}
else if(config& next_scenario = revc.child("next_scenario")) {
level_.swap(next_scenario);
}
else if(revc.has_attribute("version")) {
level_.swap(revc);
has_scenario_and_controllers = true;
}
else if(config& controllers = revc.child("controllers")) {
int index = 0;
for (const config& controller : controllers.child_range("controller")) {
if(config& side = get_scenario().child("side", index)) {
side["is_local"] = controller["is_local"];
}
++index;
}
has_scenario_and_controllers = true;
}
}
DBG_MP << "download_level_data() success.\n";
return true;
}
config& wait::get_scenario()
{
if(config& scenario = level_.child("scenario"))
return scenario;
else if(config& snapshot = level_.child("snapshot"))
return snapshot;
else
return level_;
}
const config& wait::get_scenario() const
{
if(const config& scenario = level_.child("scenario"))
return scenario;
else if(const config& snapshot = level_.child("snapshot"))
return snapshot;
else
return level_;
}
} // namespace mp

View file

@ -1,62 +0,0 @@
/*
Copyright (C) 2007 - 2016 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 as published by
the Free Software Foundation; either version 2 of the License, 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.
*/
#ifndef MULTIPLAYER_WAIT_HPP_INCLUDED
#define MULTIPLAYER_WAIT_HPP_INCLUDED
#include "flg_manager.hpp"
#include "multiplayer_ui.hpp"
#include "widgets/combo.hpp"
#include "show_dialog.hpp" //gui::preview_pane
namespace mp {
class wait : public ui
{
public:
wait(CVideo& v, wesnothd_connection* connection, const config& cfg, saved_game& state, chat& c,
config& gamelist, const bool first_scenario = true);
~wait();
virtual void process_event();
void join_game(bool observe);
void start_game();
protected:
virtual void layout_children(const SDL_Rect& rect);
virtual void hide_children(bool hide=true);
virtual void process_network_data(const config& data);
private:
void generate_menu();
bool download_level_data();
config& get_scenario();
const config& get_scenario() const;
gui::button cancel_button_;
gui::label start_label_;
gui::menu game_menu_;
config level_;
saved_game& state_;
const bool first_scenario_;
bool stop_updates_;
void process_event_impl(bool);
};
}
#endif

View file

@ -48,6 +48,7 @@
#include "game_initialization/create_engine.hpp"
#include "game_initialization/playcampaign.hpp" // for play_game, etc
#include "preferences.hpp" // for disable_preferences_save, etc
#include "preferences_display.hpp"
#include "savegame.hpp" // for clean_saves, etc
#include "scripting/application_lua_kernel.hpp"
#include "sdl/utils.hpp" // for surface