Added some missing files.
This commit is contained in:
parent
9283ba2423
commit
9ad4a96b50
4 changed files with 1000 additions and 0 deletions
436
src/multiplayer_create.cpp
Normal file
436
src/multiplayer_create.cpp
Normal file
|
@ -0,0 +1,436 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C)
|
||||
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.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "show_dialog.hpp"
|
||||
#include "multiplayer_create.hpp"
|
||||
#include "filesystem.hpp"
|
||||
#include "preferences.hpp"
|
||||
|
||||
namespace {
|
||||
const SDL_Rect null_rect = {0, 0, 0, 0};
|
||||
}
|
||||
|
||||
namespace mp {
|
||||
|
||||
create::create(display& disp, const config &cfg, chat& c, config& gamelist) :
|
||||
ui(disp, cfg, c, gamelist),
|
||||
|
||||
map_selection_(-1),
|
||||
|
||||
maps_menu_(disp, std::vector<std::string>()),
|
||||
turns_slider_(disp),
|
||||
turns_label_(disp, "", font::SIZE_SMALL, font::GOOD_COLOUR),
|
||||
village_gold_slider_(disp),
|
||||
village_gold_label_(disp, "", font::SIZE_SMALL, font::GOOD_COLOUR),
|
||||
xp_modifier_slider_(disp),
|
||||
xp_modifier_label_(disp, "", font::SIZE_SMALL, font::GOOD_COLOUR),
|
||||
name_entry_label_(disp, _("Name of game") + std::string(":"), font::SIZE_SMALL, font::GOOD_COLOUR),
|
||||
num_players_label_(disp, "", font::SIZE_SMALL, font::GOOD_COLOUR),
|
||||
era_label_(disp, _("Era") + std::string(":"), font::SIZE_SMALL, font::GOOD_COLOUR),
|
||||
map_label_(disp, _("Map to play") + std::string(":"), font::SIZE_SMALL, font::GOOD_COLOUR),
|
||||
fog_game_(disp, _("Fog Of War"), gui::button::TYPE_CHECK),
|
||||
shroud_game_(disp, _("Shroud"), gui::button::TYPE_CHECK),
|
||||
observers_game_(disp, _("Observers"), gui::button::TYPE_CHECK),
|
||||
cancel_game_(disp, _("Cancel")),
|
||||
launch_game_(disp, _("OK")),
|
||||
regenerate_map_(disp, _("Regenerate")),
|
||||
generator_settings_(disp, _("Settings...")),
|
||||
era_combo_(disp, std::vector<std::string>()),
|
||||
vision_combo_(disp, std::vector<std::string>()),
|
||||
name_entry_(disp, 32),
|
||||
minimap_restorer_(NULL),
|
||||
minimap_rect_(null_rect),
|
||||
generator_(NULL)
|
||||
{
|
||||
//build the list of scenarios to play
|
||||
get_files_in_dir(get_user_data_dir() + "/editor/maps",&user_maps_,NULL,FILE_NAME_ONLY);
|
||||
|
||||
map_options_ = user_maps_;
|
||||
|
||||
const config::child_list& levels = cfg.get_children("multiplayer");
|
||||
for(config::child_list::const_iterator j = levels.begin(); j != levels.end(); ++j){
|
||||
map_options_.push_back((**j)["name"]);
|
||||
}
|
||||
//add the 'load game' option
|
||||
map_options_.push_back(_("Load Game") + std::string("..."));
|
||||
|
||||
//create the scenarios menu
|
||||
maps_menu_.set_items(map_options_);
|
||||
maps_menu_.move_selection(0);
|
||||
maps_menu_.set_numeric_keypress_selection(false);
|
||||
|
||||
turns_slider_.set_min(20);
|
||||
turns_slider_.set_max(100);
|
||||
turns_slider_.set_value(50);
|
||||
turns_slider_.set_help_string(_("The maximum turns the game will go for"));
|
||||
|
||||
village_gold_slider_.set_min(1);
|
||||
village_gold_slider_.set_max(5);
|
||||
village_gold_slider_.set_value(1);
|
||||
village_gold_slider_.set_help_string(_("The amount of income each village yields per turn"));
|
||||
xp_modifier_slider_.set_min(25);
|
||||
xp_modifier_slider_.set_max(200);
|
||||
xp_modifier_slider_.set_value(100);
|
||||
xp_modifier_slider_.set_increment(10);
|
||||
xp_modifier_slider_.set_help_string(_("The amount of experience a unit needs to advance"));
|
||||
|
||||
fog_game_.set_check(false);
|
||||
fog_game_.set_help_string(_("Enemy units cannot be seen unless they are in range of your units"));
|
||||
|
||||
shroud_game_.set_check(false);
|
||||
shroud_game_.set_help_string(_("The map is unknown until your units explore it"));
|
||||
|
||||
observers_game_.set_check(true);
|
||||
observers_game_.set_help_string(_("Allow users who are not playing to watch the game"));
|
||||
|
||||
// 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"));
|
||||
vision_combo_.set_items(vision_types);
|
||||
vision_combo_.set_selected(0);
|
||||
|
||||
// The possible eras to play
|
||||
const config::child_list& era_list = cfg.get_children("era");
|
||||
std::vector<std::string> eras;
|
||||
for(config::child_list::const_iterator er = era_list.begin(); er != era_list.end(); ++er) {
|
||||
eras.push_back((**er)["name"]);
|
||||
}
|
||||
if(eras.empty()) {
|
||||
gui::show_dialog(disp, NULL, "", _("No multiplayer sides."), gui::OK_ONLY);
|
||||
std::cerr << "ERROR: no eras found\n";
|
||||
throw config::error(_("No eras found"));
|
||||
}
|
||||
era_combo_.set_items(eras);
|
||||
era_combo_.set_selected(0);
|
||||
|
||||
string_map i18n_symbols;
|
||||
i18n_symbols["login"] = preferences::login();
|
||||
name_entry_.set_text(vgettext("$login's game", i18n_symbols));
|
||||
|
||||
gamelist_updated();
|
||||
}
|
||||
|
||||
create::parameters& create::get_parameters()
|
||||
{
|
||||
const config::child_list& era_list = game_config().get_children("era");
|
||||
const int turns = turns_slider_.value() < turns_slider_.max_value() ?
|
||||
turns_slider_.value() : -1;
|
||||
|
||||
// Updates the values in the "parameters_" member to match the values
|
||||
// selected by the user with the widgets:
|
||||
parameters_.name = name_entry_.text();
|
||||
if (size_t(era_combo_.selected()) >= era_list.size()) {
|
||||
throw config::error(_("Invalid era selected"));
|
||||
}
|
||||
parameters_.era = (*era_list[era_combo_.selected()])["id"];
|
||||
parameters_.num_turns = turns;
|
||||
parameters_.village_gold = village_gold_slider_.value();
|
||||
parameters_.xp_modifier = xp_modifier_slider_.value();
|
||||
parameters_.fog_game = fog_game_.checked();
|
||||
parameters_.shroud_game = shroud_game_.checked();
|
||||
parameters_.allow_observers = observers_game_.checked();
|
||||
parameters_.share_view = vision_combo_.selected() == 0;
|
||||
parameters_.share_maps = vision_combo_.selected() == 1;
|
||||
|
||||
return parameters_;
|
||||
}
|
||||
|
||||
void create::process_event()
|
||||
{
|
||||
if(cancel_game_.pressed()) {
|
||||
set_result(QUIT);
|
||||
return;
|
||||
}
|
||||
|
||||
if(launch_game_.pressed() || maps_menu_.double_clicked()) {
|
||||
if(name_entry_.text() != "") {
|
||||
set_result(CREATE);
|
||||
return;
|
||||
} else {
|
||||
gui::show_dialog(disp(), NULL, "", _("You must enter a name."), gui::OK_ONLY);
|
||||
}
|
||||
}
|
||||
|
||||
// Turns per game
|
||||
const int cur_turns = turns_slider_.value();
|
||||
|
||||
std::stringstream buf;
|
||||
if(cur_turns < 100) {
|
||||
buf << _("Turns") << ": " << cur_turns;
|
||||
} else {
|
||||
buf << _("Unlimited Turns");
|
||||
}
|
||||
turns_label_.set_text(buf.str());
|
||||
|
||||
//Villages can produce between 1 and 10 gold a turn
|
||||
const int village_gold = village_gold_slider_.value();
|
||||
buf.str("");
|
||||
buf << _("Village Gold") << ": " << village_gold;
|
||||
village_gold_label_.set_text(buf.str());
|
||||
|
||||
//experience modifier
|
||||
const int xpmod = xp_modifier_slider_.value();
|
||||
buf.str("");
|
||||
buf << _("Experience Modifier") << ": " << xpmod << "%";
|
||||
xp_modifier_label_.set_text(buf.str());
|
||||
|
||||
bool map_changed = map_selection_ != maps_menu_.selection();
|
||||
map_selection_ = maps_menu_.selection();
|
||||
|
||||
if(map_changed) {
|
||||
generator_.assign(NULL);
|
||||
|
||||
tooltips::clear_tooltips(minimap_rect_);
|
||||
|
||||
const size_t select = size_t(maps_menu_.selection());
|
||||
|
||||
if(select < user_maps_.size()) {
|
||||
parameters_.saved_game = false;
|
||||
const config* const generic_multiplayer = game_config().child("generic_multiplayer");
|
||||
if(generic_multiplayer != NULL) {
|
||||
parameters_.scenario_data = *generic_multiplayer;
|
||||
parameters_.scenario_data["map_data"] = read_map(user_maps_[select]);
|
||||
}
|
||||
|
||||
} else if(select != maps_menu_.nitems()-1) {
|
||||
parameters_.saved_game = false;
|
||||
const size_t index = select - user_maps_.size();
|
||||
const config::child_list& levels = game_config().get_children("multiplayer");
|
||||
|
||||
if(index < levels.size()) {
|
||||
|
||||
parameters_.scenario_data = *levels[index];
|
||||
|
||||
std::string& map_data = parameters_.scenario_data["map_data"];
|
||||
if(map_data == "" && parameters_.scenario_data["map"] != "") {
|
||||
map_data = read_map(parameters_.scenario_data["map"]);
|
||||
}
|
||||
|
||||
//if the map should be randomly generated
|
||||
if(parameters_.scenario_data["map_generation"] != "") {
|
||||
generator_.assign(create_map_generator(parameters_.scenario_data["map_generation"],parameters_.scenario_data.child("generator")));
|
||||
}
|
||||
|
||||
if(parameters_.scenario_data["description"].empty() == false) {
|
||||
tooltips::add_tooltip(minimap_rect_,parameters_.scenario_data["description"]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parameters_.scenario_data.clear();
|
||||
parameters_.saved_game = true;
|
||||
|
||||
if (minimap_restorer_ != NULL)
|
||||
minimap_restorer_->restore();
|
||||
}
|
||||
}
|
||||
|
||||
if(generator_ != NULL && generator_->allow_user_config() && generator_settings_.pressed()) {
|
||||
generator_->user_config(disp());
|
||||
map_changed = true;
|
||||
}
|
||||
|
||||
if(generator_ != NULL && (map_changed || regenerate_map_.pressed())) {
|
||||
const cursor::setter cursor_setter(cursor::WAIT);
|
||||
|
||||
//generate the random map
|
||||
parameters_.scenario_data = generator_->create_scenario(std::vector<std::string>());
|
||||
map_changed = true;
|
||||
|
||||
//set the scenario to have placing of sides based on the terrain they prefer
|
||||
parameters_.scenario_data["modify_placing"] = "true";
|
||||
}
|
||||
|
||||
if(map_changed) {
|
||||
generator_settings_.hide(generator_ == NULL);
|
||||
regenerate_map_.hide(generator_ == NULL);
|
||||
|
||||
const std::string& map_data = parameters_.scenario_data["map_data"];
|
||||
gamemap map(game_config(), map_data);
|
||||
|
||||
//if there are less sides in the configuration than there are starting
|
||||
//positions, then generate the additional sides
|
||||
const int map_positions = map.num_valid_starting_positions();
|
||||
|
||||
for(int pos = parameters_.scenario_data.get_children("side").size(); pos < map_positions; ++pos) {
|
||||
config& side = parameters_.scenario_data.add_child("side");
|
||||
side["enemy"] = "1";
|
||||
char buf[50];
|
||||
sprintf(buf,"%d",(pos+1));
|
||||
side["side"] = buf;
|
||||
side["team_name"] = buf;
|
||||
side["canrecruit"] = "1";
|
||||
side["controller"] = "human";
|
||||
}
|
||||
|
||||
//if there are too many sides, remove some
|
||||
while(int(parameters_.scenario_data.get_children("side").size()) > map_positions) {
|
||||
parameters_.scenario_data.remove_child("side",
|
||||
parameters_.scenario_data.get_children("side").size()-1);
|
||||
}
|
||||
|
||||
const surface mini(image::getMinimap(minimap_rect_.w,minimap_rect_.h,map,0));
|
||||
if(mini != NULL) {
|
||||
SDL_Rect rect = minimap_rect_;
|
||||
SDL_BlitSurface(mini, NULL, disp().video().getSurface(), &rect);
|
||||
update_rect(rect);
|
||||
}
|
||||
const int nsides = parameters_.scenario_data.get_children("side").size();
|
||||
std::stringstream players;
|
||||
players << _("Players") << ": " << nsides;
|
||||
num_players_label_.set_text(players.str());
|
||||
}
|
||||
}
|
||||
|
||||
void create::hide_children(bool hide)
|
||||
{
|
||||
ui::hide_children(hide);
|
||||
|
||||
maps_menu_.hide(hide);
|
||||
turns_slider_.hide(hide);
|
||||
turns_label_.hide(hide);
|
||||
village_gold_slider_.hide(hide);
|
||||
village_gold_label_.hide(hide);
|
||||
xp_modifier_slider_.hide(hide);
|
||||
xp_modifier_label_.hide(hide);
|
||||
|
||||
name_entry_label_.hide(hide);
|
||||
num_players_label_.hide(hide);
|
||||
era_label_.hide(hide);
|
||||
map_label_.hide(hide);
|
||||
|
||||
fog_game_.hide(hide);
|
||||
shroud_game_.hide(hide);
|
||||
observers_game_.hide(hide);
|
||||
cancel_game_.hide(hide);
|
||||
launch_game_.hide(hide);
|
||||
regenerate_map_.hide(hide);
|
||||
generator_settings_.hide(hide);
|
||||
|
||||
era_combo_.hide(hide);
|
||||
vision_combo_.hide(hide);
|
||||
name_entry_.hide(hide);
|
||||
|
||||
if (hide) {
|
||||
minimap_restorer_.assign(NULL);
|
||||
} else {
|
||||
minimap_restorer_.assign(new surface_restorer(&disp().video(), minimap_rect_));
|
||||
|
||||
const std::string& map_data = parameters_.scenario_data["map_data"];
|
||||
gamemap map(game_config(), map_data);
|
||||
const surface mini(image::getMinimap(minimap_rect_.w,minimap_rect_.h,map,0));
|
||||
if(mini != NULL) {
|
||||
SDL_Rect rect = minimap_rect_;
|
||||
SDL_BlitSurface(mini, NULL, disp().video().getSurface(), &rect);
|
||||
update_rect(rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void create::layout_children(const SDL_Rect& rect)
|
||||
{
|
||||
ui::layout_children(rect);
|
||||
SDL_Rect ca = client_area();
|
||||
|
||||
const int border_size = 6;
|
||||
int xpos = ca.x;
|
||||
int ypos = ca.y;
|
||||
|
||||
// Dialog title
|
||||
ypos += gui::draw_dialog_title(xpos, ypos, &disp(), _("Create Game")).h + border_size;
|
||||
|
||||
// Name Entry
|
||||
name_entry_label_.set_location(xpos, ypos);
|
||||
ypos += name_entry_label_.height() + border_size;
|
||||
name_entry_.set_location(xpos, ypos);
|
||||
name_entry_.set_width(ca.w);
|
||||
ypos += name_entry_.height() + border_size;
|
||||
|
||||
// Save ypos here (column top)
|
||||
int ypos_columntop = ypos;
|
||||
|
||||
// First column: minimap & era choice
|
||||
const int minimap_width = 200;
|
||||
SDL_Rect mmrect = { xpos, ypos, minimap_width, minimap_width };
|
||||
minimap_rect_ = mmrect;
|
||||
ypos += minimap_width + border_size;
|
||||
num_players_label_.set_location(xpos, ypos);
|
||||
ypos += num_players_label_.height() + border_size;
|
||||
era_label_.set_location(xpos, ypos + (era_combo_.height() - era_label_.height()) / 2);
|
||||
era_combo_.set_location(xpos + era_label_.width() + border_size, ypos);
|
||||
|
||||
// Second column: map menu
|
||||
ypos = ypos_columntop + border_size;
|
||||
xpos += minimap_width + border_size;
|
||||
map_label_.set_location(xpos, ypos);
|
||||
ypos += map_label_.height() + border_size;
|
||||
maps_menu_.set_max_width(200);
|
||||
maps_menu_.set_max_height(ca.x + ca.h - ypos);
|
||||
maps_menu_.set_location(xpos, ypos);
|
||||
// Menu dimensions are only updated when items are set. So do this now.
|
||||
maps_menu_.set_items(map_options_);
|
||||
maps_menu_.move_selection(map_selection_);
|
||||
|
||||
// Third column: big buch of options
|
||||
ypos = ypos_columntop + border_size;
|
||||
xpos += 200 + border_size;
|
||||
|
||||
turns_label_.set_location(xpos, ypos);
|
||||
ypos += turns_label_.height() + border_size;
|
||||
turns_slider_.set_width(ca.w - xpos);
|
||||
turns_slider_.set_location(xpos, ypos);
|
||||
ypos += turns_slider_.height() + border_size;
|
||||
|
||||
village_gold_label_.set_location(xpos, ypos);
|
||||
ypos += village_gold_label_.height() + border_size;
|
||||
village_gold_slider_.set_width(ca.w - xpos);
|
||||
village_gold_slider_.set_location(xpos, ypos);
|
||||
ypos += village_gold_slider_.height() + border_size;
|
||||
|
||||
xp_modifier_label_.set_location(xpos, ypos);
|
||||
ypos += xp_modifier_label_.height() + border_size;
|
||||
xp_modifier_slider_.set_width(ca.w - xpos);
|
||||
xp_modifier_slider_.set_location(xpos, ypos);
|
||||
ypos += xp_modifier_slider_.height() + border_size;
|
||||
|
||||
fog_game_.set_location(xpos, ypos);
|
||||
ypos += fog_game_.height() + border_size;
|
||||
|
||||
shroud_game_.set_location(xpos, ypos);
|
||||
ypos += shroud_game_.height() + border_size;
|
||||
|
||||
observers_game_.set_location(xpos, ypos);
|
||||
ypos += observers_game_.height() + border_size;
|
||||
|
||||
vision_combo_.set_location(xpos, ypos);
|
||||
ypos += vision_combo_.height() + border_size;
|
||||
|
||||
regenerate_map_.set_location(xpos, ypos);
|
||||
ypos += regenerate_map_.height() + border_size;
|
||||
generator_settings_.set_location(xpos, ypos);
|
||||
|
||||
// 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.w - right_button->width(), ca.y + ca.h - right_button->height());
|
||||
left_button->set_location(right_button->location().x - right_button->width(), ca.y + ca.h - right_button->height());
|
||||
}
|
||||
|
||||
}
|
116
src/multiplayer_create.hpp
Normal file
116
src/multiplayer_create.hpp
Normal file
|
@ -0,0 +1,116 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C)
|
||||
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.
|
||||
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_CREATE_HPP_INCLUDED
|
||||
#define MULTIPLAYER_CREATE_HPP_INCLUDED
|
||||
|
||||
#include "multiplayer_ui.hpp"
|
||||
#include "widgets/slider.hpp"
|
||||
#include "widgets/label.hpp"
|
||||
#include "widgets/combo.hpp"
|
||||
#include "mapgen.hpp"
|
||||
|
||||
namespace mp {
|
||||
|
||||
class create : public mp::ui, public font::floating_label_context
|
||||
{
|
||||
public:
|
||||
struct parameters
|
||||
{
|
||||
parameters() { reset(); };
|
||||
|
||||
void reset() {
|
||||
name = "";
|
||||
era = "";
|
||||
num_turns = 0;
|
||||
village_gold = 0;
|
||||
xp_modifier = 0;
|
||||
fog_game = shroud_game = allow_observers = share_view = share_maps = false;
|
||||
|
||||
scenario_data.clear();
|
||||
}
|
||||
|
||||
// The items returned while configuring the game
|
||||
|
||||
std::string name;
|
||||
std::string era;
|
||||
|
||||
int num_turns;
|
||||
int village_gold;
|
||||
int xp_modifier;
|
||||
bool fog_game;
|
||||
bool shroud_game;
|
||||
bool allow_observers;
|
||||
bool share_view;
|
||||
bool share_maps;
|
||||
|
||||
bool saved_game;
|
||||
|
||||
// If the game is to be randomly generated, the map generator
|
||||
// will create the scenario data in this variable:
|
||||
config scenario_data;
|
||||
};
|
||||
|
||||
create(display& dist, const config& game_config, chat& c, config& gamelist);
|
||||
|
||||
parameters& get_parameters();
|
||||
|
||||
protected:
|
||||
virtual void layout_children(const SDL_Rect& rect);
|
||||
virtual void process_event();
|
||||
virtual void hide_children(bool hide=true);
|
||||
|
||||
private:
|
||||
void update_minimap(void);
|
||||
|
||||
int map_selection_;
|
||||
|
||||
std::vector<std::string> user_maps_;
|
||||
std::vector<std::string> map_options_;
|
||||
|
||||
gui::menu maps_menu_;
|
||||
gui::slider turns_slider_;
|
||||
gui::label turns_label_;
|
||||
gui::slider village_gold_slider_;
|
||||
gui::label village_gold_label_;
|
||||
gui::slider xp_modifier_slider_;
|
||||
gui::label xp_modifier_label_;
|
||||
|
||||
gui::label name_entry_label_;
|
||||
gui::label num_players_label_;
|
||||
gui::label era_label_;
|
||||
gui::label map_label_;
|
||||
|
||||
gui::button fog_game_;
|
||||
gui::button shroud_game_;
|
||||
gui::button observers_game_;
|
||||
gui::button cancel_game_;
|
||||
gui::button launch_game_;
|
||||
gui::button regenerate_map_;
|
||||
gui::button generator_settings_;
|
||||
|
||||
gui::combo era_combo_;
|
||||
gui::combo vision_combo_;
|
||||
gui::textbox name_entry_;
|
||||
|
||||
util::scoped_ptr<surface_restorer> minimap_restorer_;
|
||||
SDL_Rect minimap_rect_;
|
||||
|
||||
util::scoped_ptr<map_generator> generator_;
|
||||
|
||||
parameters parameters_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
292
src/multiplayer_ui.cpp
Normal file
292
src/multiplayer_ui.cpp
Normal file
|
@ -0,0 +1,292 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C)
|
||||
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.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "multiplayer_ui.hpp"
|
||||
#include "network.hpp"
|
||||
#include "preferences.hpp"
|
||||
#include "game_config.hpp"
|
||||
#include "sound.hpp"
|
||||
|
||||
#define LOG_NW lg::info(lg::network)
|
||||
#define ERR_NW lg::err(lg::network)
|
||||
|
||||
namespace mp {
|
||||
|
||||
void check_response(network::connection res, const config& data)
|
||||
{
|
||||
if(!res) {
|
||||
throw network::error(_("Connection timed out"));
|
||||
}
|
||||
|
||||
const config* err = data.child("error");
|
||||
if(err != NULL) {
|
||||
throw network::error((*err)["message"]);
|
||||
}
|
||||
}
|
||||
|
||||
chat::chat()
|
||||
{
|
||||
}
|
||||
|
||||
void chat::add_message(const std::string& user, const std::string& message)
|
||||
{
|
||||
message_history_.push_back(msg(user, message));
|
||||
|
||||
while (message_history_.size() > 1024) {
|
||||
message_history_.pop_front();
|
||||
|
||||
if (last_update_ > 0)
|
||||
last_update_--;
|
||||
}
|
||||
}
|
||||
|
||||
void chat::init_textbox(gui::textbox& textbox)
|
||||
{
|
||||
std::string s;
|
||||
|
||||
for(msg_hist::const_iterator itor = message_history_.begin();
|
||||
itor != message_history_.end(); ++itor) {
|
||||
s.append(format_message(*itor));
|
||||
}
|
||||
|
||||
textbox.set_text(s);
|
||||
|
||||
last_update_ = message_history_.size();
|
||||
}
|
||||
|
||||
void chat::update_textbox(gui::textbox& textbox)
|
||||
{
|
||||
std::string s;
|
||||
|
||||
for(msg_hist::const_iterator itor = message_history_.begin() + last_update_;
|
||||
itor != message_history_.end(); ++itor) {
|
||||
s.append(format_message(*itor));
|
||||
}
|
||||
|
||||
textbox.append_text(s);
|
||||
|
||||
last_update_ = message_history_.size();
|
||||
}
|
||||
|
||||
std::string chat::format_message(const msg& message)
|
||||
{
|
||||
return "<" + message.user + ">" + message.message + "\n";
|
||||
}
|
||||
|
||||
ui::ui(display& disp, const config& cfg, chat& c, config& gamelist) :
|
||||
gui::widget(disp),
|
||||
|
||||
game_config_(cfg),
|
||||
chat_(c),
|
||||
gamelist_(gamelist),
|
||||
|
||||
chat_textbox_(disp, 100, "", false),
|
||||
entry_textbox_(disp, 100),
|
||||
users_menu_(disp, std::vector<std::string>()),
|
||||
|
||||
result_(CONTINUE)
|
||||
{
|
||||
chat_.init_textbox(chat_textbox_);
|
||||
}
|
||||
|
||||
void ui::process_network()
|
||||
{
|
||||
config data;
|
||||
|
||||
try {
|
||||
const network::connection sock = network::receive_data(data);
|
||||
|
||||
if(sock) {
|
||||
process_network_data(data, sock);
|
||||
}
|
||||
} catch(network::error& e) {
|
||||
process_network_error(e);
|
||||
}
|
||||
|
||||
if (accept_connections()) {
|
||||
network::connection sock = network::accept_connection();
|
||||
if(sock) {
|
||||
LOG_NW << "Received connection\n";
|
||||
|
||||
process_network_connection(sock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui::result ui::get_result()
|
||||
{
|
||||
return result_;
|
||||
}
|
||||
|
||||
ui::result ui::set_result(ui::result res)
|
||||
{
|
||||
result_ = res;
|
||||
return res;
|
||||
}
|
||||
|
||||
int ui::xscale(int x) const
|
||||
{
|
||||
return (x*disp().x())/1024;
|
||||
}
|
||||
|
||||
int ui::yscale(int y) const
|
||||
{
|
||||
return (y*disp().y())/768;
|
||||
}
|
||||
|
||||
SDL_Rect ui::client_area() const
|
||||
{
|
||||
SDL_Rect res = { xscale(11)+6, yscale(40)+6, xscale(833)-12, yscale(524)-12 };
|
||||
|
||||
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",image::UNSCALED));
|
||||
background = scale_surface(background, disp().x(), disp().y());
|
||||
if(background == NULL)
|
||||
return;
|
||||
SDL_BlitSurface(background, NULL, disp().video().getSurface(), NULL);
|
||||
update_whole_screen();
|
||||
|
||||
hide_children(false);
|
||||
}
|
||||
|
||||
void ui::set_location(const SDL_Rect& rect)
|
||||
{
|
||||
hide_children();
|
||||
widget::set_location(rect);
|
||||
layout_children(rect);
|
||||
hide_children(false);
|
||||
}
|
||||
|
||||
void ui::process_event()
|
||||
{
|
||||
}
|
||||
|
||||
void ui::handle_event(const SDL_Event& event)
|
||||
{
|
||||
if(event.type == SDL_KEYDOWN) {
|
||||
handle_key_event(event.key);
|
||||
}
|
||||
}
|
||||
|
||||
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 && !entry_textbox_.text().empty()) {
|
||||
|
||||
// Sends the message to the network
|
||||
config msg;
|
||||
msg["message"] = entry_textbox_.text();
|
||||
msg["sender"] = preferences::login();
|
||||
config data;
|
||||
data.add_child("message", msg);
|
||||
network::send_data(data);
|
||||
|
||||
chat_.add_message(preferences::login(), entry_textbox_.text());
|
||||
chat_.update_textbox(chat_textbox_);
|
||||
entry_textbox_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void ui::process_network_data(const config& data, const network::connection sock)
|
||||
{
|
||||
if(data.child("error")) {
|
||||
throw network::error((*data.child("error"))["message"]);
|
||||
} else {
|
||||
if(data.child("message")) {
|
||||
sound::play_sound(game_config::sounds::receive_message);
|
||||
|
||||
const config& msg = *data.child("message");
|
||||
chat_.add_message(msg["sender"], msg["message"]);
|
||||
chat_.update_textbox(chat_textbox_);
|
||||
}
|
||||
|
||||
if(data.child("gamelist")) {
|
||||
gamelist_ = data;
|
||||
gamelist_updated();
|
||||
} else if(data.child("gamelist_diff")) {
|
||||
gamelist_.apply_diff(*data.child("gamelist_diff"));
|
||||
gamelist_updated();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ui::process_network_error(network::error& error)
|
||||
{
|
||||
ERR_NW << "Caught networking error: " << error.message << "\n";
|
||||
|
||||
// Default behaviour is to re-throw the error. May be overridden.
|
||||
throw error;
|
||||
}
|
||||
|
||||
void ui::process_network_connection(const network::connection sock)
|
||||
{
|
||||
LOG_NW << "Caught network connection.\n";
|
||||
}
|
||||
|
||||
void ui::hide_children(bool hide)
|
||||
{
|
||||
chat_textbox_.hide(hide);
|
||||
entry_textbox_.hide(hide);
|
||||
users_menu_.hide(hide);
|
||||
}
|
||||
|
||||
void ui::layout_children(const SDL_Rect& rect)
|
||||
{
|
||||
users_menu_.set_width(xscale(156));
|
||||
users_menu_.set_location(xscale(856),yscale(42));
|
||||
chat_textbox_.set_location(xscale(11) + 4, xscale(573) + 4);
|
||||
chat_textbox_.set_measurements(xscale(833) - 8, xscale(143) - 8);
|
||||
entry_textbox_.set_location(xscale(11) + 4,yscale(732));
|
||||
entry_textbox_.set_width(xscale(833) - 8);
|
||||
}
|
||||
|
||||
void ui::gamelist_updated()
|
||||
{
|
||||
std::vector<std::string> user_strings;
|
||||
config::child_list users = gamelist_.get_children("user");
|
||||
config::child_iterator user;
|
||||
for (user = users.begin(); user != users.end(); ++user) {
|
||||
const std::string prefix = (**user)["available"] == "no" ? "#" : "";
|
||||
user_strings.push_back(prefix + (**user)["name"]);
|
||||
}
|
||||
set_user_list(user_strings);
|
||||
}
|
||||
|
||||
void ui::set_user_list(const std::vector<std::string>& list)
|
||||
{
|
||||
const int old_users = user_list_.size();
|
||||
user_list_ = list;
|
||||
const int new_users = user_list_.size();
|
||||
|
||||
if(new_users < old_users) {
|
||||
sound::play_sound(game_config::sounds::user_leave);
|
||||
} else if(new_users > old_users) {
|
||||
sound::play_sound(game_config::sounds::user_arrive);
|
||||
}
|
||||
|
||||
users_menu_.set_items(user_list_);
|
||||
}
|
||||
|
||||
}
|
||||
|
156
src/multiplayer_ui.hpp
Normal file
156
src/multiplayer_ui.hpp
Normal file
|
@ -0,0 +1,156 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C)
|
||||
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.
|
||||
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 "multiplayer.hpp"
|
||||
#include "widgets/textbox.hpp"
|
||||
#include "widgets/button.hpp"
|
||||
#include "widgets/menu.hpp"
|
||||
#include "network.hpp"
|
||||
#include "config.hpp"
|
||||
|
||||
#include <deque>
|
||||
#include <string>
|
||||
|
||||
namespace mp {
|
||||
|
||||
void check_response(network::connection res, const config& data);
|
||||
|
||||
//this class memorizes a chat session.
|
||||
class chat
|
||||
{
|
||||
public:
|
||||
chat();
|
||||
|
||||
void add_message(const std::string& user, const std::string& message);
|
||||
|
||||
void init_textbox(gui::textbox& textbox);
|
||||
void update_textbox(gui::textbox& textbox);
|
||||
|
||||
private:
|
||||
struct msg {
|
||||
msg(const std::string& user, const std::string& message) :
|
||||
user(user), message(message) {};
|
||||
std::string user;
|
||||
std::string message;
|
||||
};
|
||||
typedef std::deque<msg> msg_hist;
|
||||
|
||||
std::string format_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
|
||||
{
|
||||
public:
|
||||
enum result { CONTINUE, JOIN, OBSERVE, CREATE, PLAY, QUIT };
|
||||
|
||||
ui(display& d, 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);
|
||||
using widget::set_location;
|
||||
|
||||
protected:
|
||||
int xscale(int x) const;
|
||||
int yscale(int y) const;
|
||||
|
||||
SDL_Rect client_area() const;
|
||||
|
||||
// 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();
|
||||
|
||||
virtual void process_event();
|
||||
|
||||
virtual void handle_event(const SDL_Event& event);
|
||||
virtual void handle_key_event(const SDL_KeyboardEvent& event);
|
||||
|
||||
// Processes any pending network data. Called by the public
|
||||
// process_network() method. Overridden by subclasses who add more
|
||||
// behaviour for network.
|
||||
virtual void process_network_data(const config& data, const network::connection sock);
|
||||
|
||||
// Processes any pending network error. Called by the public
|
||||
// process_network() method. Overridden by subclasses
|
||||
virtual void process_network_error(network::error& error);
|
||||
|
||||
// Return true if we must accept incoming connections, false if not.
|
||||
// Defaults to not.
|
||||
virtual bool accept_connections() { return false; };
|
||||
|
||||
// Processes a pending network connection.
|
||||
virtual void process_network_connection(const network::connection sock);
|
||||
|
||||
// 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);
|
||||
|
||||
// Called each time the gamelist_ variable is updated. May be
|
||||
// overridden by child classes to add custom gamelist behaviour.
|
||||
virtual void gamelist_updated();
|
||||
|
||||
// Sets the user list
|
||||
void set_user_list(const std::vector<std::string>&);
|
||||
|
||||
// Returns the current gamelist
|
||||
config& gamelist() { return gamelist_; };
|
||||
private:
|
||||
// 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::textbox chat_textbox_;
|
||||
gui::textbox entry_textbox_;
|
||||
|
||||
gui::menu users_menu_;
|
||||
|
||||
std::vector<std::string> user_list_;
|
||||
|
||||
result result_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Add table
Reference in a new issue