wesnoth/src/multiplayer_ui.cpp
Rusty Russell e2bdce1542 Partial revert of 2006-10-10T19:57:14Z!joerg.hinrichs@alice-dsl.de which causes OOS (see #7433). 1.2 only.
Joerg: please revert this once you've found the problem!

Unfortunately that commit 2006-10-10T19:57:14Z!joerg.hinrichs@alice-dsl.de did several things.  This keeps:
1) The renaming of status to gamestatus,
2) The use of gamestatus's snapshot member rather than a standalone.
3) Changes to firing of events in replays
2006-10-16 12:22:13 +00:00

491 lines
12 KiB
C++

/* $Id$ */
/*
Copyright (C) 2005
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 "global.hpp"
#include "display.hpp"
#include "gettext.hpp"
#include "game_config.hpp"
#include "image.hpp"
#include "log.hpp"
#include "multiplayer_ui.hpp"
#include "network.hpp"
#include "sound.hpp"
#include "video.hpp"
#include "replay.hpp"
#define LOG_NW LOG_STREAM(info, network)
#define ERR_NW LOG_STREAM(err, 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"]);
}
}
void level_to_gamestate(config& level, game_state& state)
{
//any replay data is only temporary and should be removed from
//the level data in case we want to save the game later
config * const replay_data = level.child("replay");
config replay_data_store;
if(replay_data != NULL) {
replay_data_store = *replay_data;
LOG_NW << "setting replay\n";
recorder = replay(replay_data_store);
if(!recorder.empty()) {
recorder.set_skip(false);
}
level.clear_children("replay");
}
//adds the starting pos to the level
if(level.child("replay_start") == NULL)
level.add_child("replay_start") = level;
level["campaign_type"] = "multiplayer";
state.campaign_type = "multiplayer";
state.snapshot = level;
}
std::string get_colour_string(int id)
{
std::string prefix("\033[3 m");
prefix[3] = lexical_cast<char, int>(id + 1);
std::map<int, std::string>::iterator name = game_config::team_rgb_name.find(id+1);
if(name != game_config::team_rgb_name.end()){
return prefix + _(name->second.c_str());
}else{
return prefix + _("Invalid Color");
}
}
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();
textbox.scroll_to_bottom();
}
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,true);
last_update_ = message_history_.size();
}
std::string chat::format_message(const msg& message)
{
if(message.message.substr(0,3) == "/me") {
return "<" + message.user + " " + message.message.substr(3) + ">\n";
} else {
return "<" + message.user + ">" + message.message + "\n";
}
}
ui::ui(display& disp, const std::string& title, const config& cfg, chat& c, config& gamelist) :
gui::widget(disp.video()),
disp_(disp),
initialized_(false),
gamelist_initialized_(false),
hotkey_handler_(&disp),
disp_manager_(&disp),
game_config_(cfg),
chat_(c),
gamelist_(gamelist),
title_(disp.video(), title, font::SIZE_LARGE, font::TITLE_COLOUR),
chat_textbox_(disp.video(), 100, "", false),
entry_textbox_(disp.video(), 100),
users_menu_(disp.video(), std::vector<std::string>(), false, -1, -1, NULL, &gui::menu::slateborder_style),
result_(CONTINUE)
{
const SDL_Rect area = { 0, 0, disp.video().getx(), disp.video().gety() };
set_location(area);
}
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 * width())/1024;
}
int ui::yscale(int y) const
{
return (y * height())/768;
}
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",image::UNSCALED));
background = scale_surface(background, video().getx(), video().gety());
if(background == NULL)
return;
SDL_BlitSurface(background, NULL, 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);
if(!initialized_) {
chat_textbox_.set_wrap(true);
chat_.init_textbox(chat_textbox_);
initialized_ = true;
}
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::send_chat_query(const std::string& args)
{
config data;
data.add_child("query")["type"] = args;
network::send_data(data);
}
void ui::add_chat_message(const std::string& speaker, int /*side*/, const std::string& message, display::MESSAGE_TYPE /*type*/)
{
chat_.add_message(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(preferences::login(),0, message); //local echo
network::send_data(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 && !entry_textbox_.text().empty()) {
chat_handler::do_speak(entry_textbox_.text());
entry_textbox_.clear();
} else if(event.keysym.sym == SDLK_TAB ) {
std::string text = entry_textbox_.text();
std::string semiword;
bool beginning;
const size_t last_space = text.rfind(" ");
//if last character is a space return
if(last_space == text.size() -1) {
return;
}
if(last_space == std::string::npos) {
beginning = true;
semiword = text;
}else{
beginning = false;
semiword.assign(text,last_space+1,text.size());
}
std::vector<std::string> matches;
std::string best_match = semiword;
std::vector<std::string>& users = user_list_;
std::sort<std::vector<std::string>::iterator>(users.begin(), users.end());
for(std::vector<std::string>::const_iterator i = users.begin(); i != users.end(); ++i) {
if( i->size() >= semiword.size() &&
std::equal(semiword.begin(),semiword.end(),i->begin(),chars_equal_insensitive)) {
if(matches.empty()) {
best_match = *i;
} else {
int j= 0;;
while(toupper(best_match[j]) == toupper((*i)[j])) j++;
best_match.erase(best_match.begin()+j,best_match.end());
}
matches.push_back(*i);
}
}
if(!matches.empty()) {
std::string add = beginning ? ": " : " ";
text.replace(last_space+1, semiword.size(), best_match);
if(matches.size() == 1) {
text.append(add);
} else {
std::string completion_list;
std::vector<std::string>::iterator it;
for(it =matches.begin();it!=matches.end();it++) {
completion_list += " ";
completion_list += *it;
}
chat_.add_message("",completion_list);
chat_.update_textbox(chat_textbox_);
}
entry_textbox_.set_text(text);
}
}
}
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")) {
const config& msg = *data.child("message");
config* cignore;
bool ignored = false;
if ((cignore = preferences::get_prefs()->child("ignore"))){
for(std::map<std::string,t_string>::const_iterator i = cignore->values.begin();
i != cignore->values.end(); ++i){
if(msg["sender"] == i->first){
if (i->second == "yes"){
ignored = true;
}
}
}
}
if (!ignored){
sound::play_sound(game_config::sounds::receive_message);
chat_.add_message(msg["sender"], msg["message"]);
chat_.update_textbox(chat_textbox_);
}
}
if(data.child("whisper")){
const config& cwhisper = *data.child("whisper");
config* cignore;
bool ignored = false;
if ((cignore = preferences::get_prefs()->child("ignore"))){
for(std::map<std::string,t_string>::const_iterator i = cignore->values.begin();
i != cignore->values.end(); ++i){
if(cwhisper["sender"] == i->first){
if (i->second == "yes"){
ignored = true;
}
}
}
}
if (!ignored){
sound::play_sound(game_config::sounds::receive_message);
chat_.add_message("whisper: "+cwhisper["sender"], cwhisper["message"]);
chat_.update_textbox(chat_textbox_);
}
}
if(data.child("gamelist")) {
if(!gamelist_initialized_)
gamelist_initialized_ = true;
gamelist_ = data;
gamelist_updated(false);
} else if(data.child("gamelist_diff")) {
if(gamelist_initialized_) {
gamelist_.apply_diff(*data.child("gamelist_diff"));
gamelist_updated(false);
}
}
}
}
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)
{
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);
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);
}
void ui::gamelist_updated(bool silent)
{
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" ? "#" : "";
std::string suffix = "";
if(!(**user)["location"].empty()) {
suffix = std::string(" (") + (**user)["location"] + std::string(")");
}
user_strings.push_back(prefix + (**user)["name"].str() + suffix);
}
set_user_list(user_strings, silent);
}
void ui::set_user_list(const std::vector<std::string>& list, bool silent)
{
if(!silent) {
if(list.size() < user_list_.size()) {
sound::play_sound(game_config::sounds::user_leave);
} else if(list.size() > user_list_.size()) {
sound::play_sound(game_config::sounds::user_arrive);
}
}
user_list_ = list;
users_menu_.set_items(user_list_,true,true);
}
const gui::widget& ui::title() const
{
return title_;
}
}