
Kill set_wm_icon function, since SDL_WM_SetIcon can be called safely only at one place. Clearly hardcode the image's url instead of pretending that we use game_config. Rename the image to "game-icon.png" (since you are forced to use it).
2471 lines
75 KiB
C++
2471 lines
75 KiB
C++
/* $Id$ */
|
|
/*
|
|
Copyright (C) 2003 - 2010 by David White <dave@whitevine.net>
|
|
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License version 2
|
|
or at your option any later version.
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY.
|
|
|
|
See the COPYING file for more details.
|
|
*/
|
|
|
|
#include "global.hpp"
|
|
|
|
#include "SDL.h"
|
|
#include "SDL_mixer.h"
|
|
|
|
#ifndef DISABLE_EDITOR
|
|
#include "SDL_getenv.h"
|
|
#endif
|
|
|
|
#include "about.hpp"
|
|
#include "ai/configuration.hpp"
|
|
#include "config.hpp"
|
|
#include "config_cache.hpp"
|
|
#include "construct_dialog.hpp"
|
|
#include "cursor.hpp"
|
|
#include "dialogs.hpp"
|
|
#include "foreach.hpp"
|
|
#include "game_display.hpp"
|
|
#include "builder.hpp"
|
|
#include "filesystem.hpp"
|
|
#include "font.hpp"
|
|
#include "formula.hpp"
|
|
#include "game_config.hpp"
|
|
#include "game_errors.hpp"
|
|
#include "gamestatus.hpp"
|
|
#include "gettext.hpp"
|
|
#include "gui/dialogs/addon_connect.hpp"
|
|
#include "gui/dialogs/campaign_selection.hpp"
|
|
#include "gui/dialogs/language_selection.hpp"
|
|
#include "gui/dialogs/message.hpp"
|
|
#include "gui/dialogs/mp_method_selection.hpp"
|
|
#include "gui/dialogs/title_screen.hpp"
|
|
#include "gui/dialogs/transient_message.hpp"
|
|
#ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
|
|
#include "gui/widgets/debug.hpp"
|
|
#endif
|
|
#include "gui/auxiliary/event/handler.hpp"
|
|
#include "gui/widgets/settings.hpp"
|
|
#include "gui/widgets/window.hpp"
|
|
#include "help.hpp"
|
|
#include "hotkeys.hpp"
|
|
#include "intro.hpp"
|
|
#include "language.hpp"
|
|
#include "loadscreen.hpp"
|
|
#include "log.hpp"
|
|
#include "map_exception.hpp"
|
|
#include "widgets/menu.hpp"
|
|
#include "marked-up_text.hpp"
|
|
#include "multiplayer.hpp"
|
|
#include "network.hpp"
|
|
#include "playcampaign.hpp"
|
|
#include "preferences_display.hpp"
|
|
#include "addon/manager.hpp"
|
|
#include "replay.hpp"
|
|
#include "savegame.hpp"
|
|
#include "scripting/lua.hpp"
|
|
#include "sound.hpp"
|
|
#include "statistics.hpp"
|
|
#include "thread.hpp"
|
|
#include "titlescreen.hpp"
|
|
#include "upload_log.hpp"
|
|
#include "wml_exception.hpp"
|
|
#include "wml_separators.hpp"
|
|
#include "serialization/binary_or_text.hpp"
|
|
#include "serialization/parser.hpp"
|
|
#include "serialization/preprocessor.hpp"
|
|
#include "serialization/string_utils.hpp"
|
|
#include "sha1.hpp"
|
|
#include "version.hpp"
|
|
|
|
//#ifdef _WIN32
|
|
//#include "locale.h"
|
|
//#endif
|
|
|
|
#ifndef DISABLE_EDITOR
|
|
#include "editor/editor_main.hpp"
|
|
#endif
|
|
|
|
#include "wesconfig.h"
|
|
|
|
#include <cerrno>
|
|
#include <clocale>
|
|
#include <cmath>
|
|
#include <cstdlib>
|
|
#include <ctime>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <iterator>
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
|
|
#include <boost/iostreams/copy.hpp>
|
|
#include <boost/iostreams/filtering_streambuf.hpp>
|
|
#include <boost/iostreams/filter/gzip.hpp>
|
|
|
|
// Minimum stack cookie to prevent stack overflow on AmigaOS4
|
|
#ifdef __amigaos4__
|
|
const char __attribute__((used)) stackcookie[] = "\0$STACK: 16000000";
|
|
#endif
|
|
|
|
static lg::log_domain log_config("config");
|
|
#define ERR_CONFIG LOG_STREAM(err, log_config)
|
|
#define WRN_CONFIG LOG_STREAM(warn, log_config)
|
|
#define LOG_CONFIG LOG_STREAM(info, log_config)
|
|
|
|
#define LOG_GENERAL LOG_STREAM(info, lg::general)
|
|
#define WRN_GENERAL LOG_STREAM(warn, lg::general)
|
|
#define DBG_GENERAL LOG_STREAM(debug, lg::general)
|
|
|
|
static lg::log_domain log_network("network");
|
|
#define ERR_NET LOG_STREAM(err, log_network)
|
|
|
|
static lg::log_domain log_preprocessor("preprocessor");
|
|
#define LOG_PREPROC LOG_STREAM(info,log_preprocessor)
|
|
|
|
static bool less_campaigns_rank(const config &a, const config &b) {
|
|
return a["rank"].to_int(1000) < b["rank"].to_int(1000);
|
|
}
|
|
|
|
namespace {
|
|
struct jump_to_campaign_info
|
|
{
|
|
public:
|
|
jump_to_campaign_info(bool jump,int difficulty, std::string campaign_id,std::string scenario_id)
|
|
: jump_(jump)
|
|
, difficulty_(difficulty)
|
|
, campaign_id_(campaign_id)
|
|
, scenario_id_(scenario_id)
|
|
{
|
|
}
|
|
bool jump_;
|
|
int difficulty_;
|
|
std::string campaign_id_,scenario_id_;
|
|
};
|
|
class game_controller
|
|
{
|
|
public:
|
|
game_controller(int argc, char** argv);
|
|
~game_controller();
|
|
|
|
game_display& disp();
|
|
|
|
bool init_video();
|
|
bool init_config(const bool force=false);
|
|
bool init_language();
|
|
bool play_test();
|
|
bool play_multiplayer_mode();
|
|
bool play_screenshot_mode();
|
|
|
|
void reload_changed_game_config();
|
|
|
|
bool is_loading() const;
|
|
void clear_loaded_game() { game::load_game_exception::game.clear(); }
|
|
bool load_game();
|
|
void set_tutorial();
|
|
|
|
std::string jump_to_campaign_id() const;
|
|
bool new_campaign();
|
|
bool goto_campaign();
|
|
bool goto_multiplayer();
|
|
#ifndef DISABLE_EDITOR
|
|
bool goto_editor();
|
|
#endif
|
|
bool play_multiplayer();
|
|
bool change_language();
|
|
|
|
void show_preferences();
|
|
void show_upload_begging();
|
|
|
|
enum RELOAD_GAME_DATA { RELOAD_DATA, NO_RELOAD_DATA };
|
|
void launch_game(RELOAD_GAME_DATA reload=RELOAD_DATA);
|
|
void play_replay();
|
|
#ifndef DISABLE_EDITOR
|
|
editor::EXIT_STATUS start_editor(const std::string& filename = "");
|
|
#endif
|
|
void start_wesnothd();
|
|
const config& game_config() const { return game_config_; }
|
|
|
|
private:
|
|
game_controller(const game_controller&);
|
|
void operator=(const game_controller&);
|
|
|
|
void load_game_cfg(const bool force=false);
|
|
void set_unit_data();
|
|
|
|
void mark_completed_campaigns(std::vector<config>& campaigns);
|
|
|
|
const int argc_;
|
|
int arg_;
|
|
const char* const * const argv_;
|
|
|
|
//this should get destroyed *after* the video, since we want
|
|
//to clean up threads after the display disappears.
|
|
const threading::manager thread_manager;
|
|
|
|
CVideo video_;
|
|
|
|
const font::manager font_manager_;
|
|
const preferences::manager prefs_manager_;
|
|
const image::manager image_manager_;
|
|
const events::event_context main_event_context_;
|
|
const hotkey::manager hotkey_manager_;
|
|
const upload_log::manager upload_log_manager_;
|
|
sound::music_thinker music_thinker_;
|
|
resize_monitor resize_monitor_;
|
|
binary_paths_manager paths_manager_;
|
|
|
|
std::string test_scenario_;
|
|
|
|
bool test_mode_, multiplayer_mode_, no_gui_, screenshot_mode_;
|
|
std::string screenshot_map_, screenshot_filename_;
|
|
int force_bpp_;
|
|
|
|
config game_config_;
|
|
preproc_map old_defines_map_;
|
|
|
|
util::scoped_ptr<game_display> disp_;
|
|
|
|
game_state state_;
|
|
|
|
std::string multiplayer_server_;
|
|
bool jump_to_multiplayer_;
|
|
jump_to_campaign_info jump_to_campaign_;
|
|
#ifndef DISABLE_EDITOR
|
|
bool jump_to_editor_;
|
|
#endif
|
|
game_config::config_cache& cache_;
|
|
};
|
|
|
|
game_controller::game_controller(int argc, char** argv) :
|
|
argc_(argc),
|
|
arg_(1),
|
|
argv_(argv),
|
|
thread_manager(),
|
|
video_(),
|
|
font_manager_(),
|
|
prefs_manager_(),
|
|
image_manager_(),
|
|
main_event_context_(),
|
|
hotkey_manager_(),
|
|
upload_log_manager_(),
|
|
music_thinker_(),
|
|
resize_monitor_(),
|
|
paths_manager_(),
|
|
test_scenario_("test"),
|
|
test_mode_(false),
|
|
multiplayer_mode_(false),
|
|
no_gui_(false),
|
|
screenshot_mode_(false),
|
|
screenshot_map_(),
|
|
screenshot_filename_(),
|
|
force_bpp_(-1),
|
|
game_config_(),
|
|
old_defines_map_(),
|
|
disp_(NULL),
|
|
state_(),
|
|
multiplayer_server_(),
|
|
jump_to_multiplayer_(false),
|
|
jump_to_campaign_(false,-1,"","")
|
|
#ifndef DISABLE_EDITOR
|
|
,jump_to_editor_(false)
|
|
#endif
|
|
,cache_(game_config::config_cache::instance())
|
|
{
|
|
bool no_music = false;
|
|
bool no_sound = false;
|
|
|
|
// The path can be hardcoded and it might be a relative path.
|
|
if(!game_config::path.empty() &&
|
|
#ifdef _WIN32
|
|
// use c_str to ensure that index 1 points to valid element since c_str() returns null-terminated string
|
|
game_config::path.c_str()[1] != ':'
|
|
#else
|
|
game_config::path[0] != '/'
|
|
#endif
|
|
)
|
|
{
|
|
game_config::path = get_cwd() + '/' + game_config::path;
|
|
font_manager_.update_font_path();
|
|
}
|
|
|
|
#ifndef DISABLE_EDITOR
|
|
const std::string app_basename = file_name(argv[0]);
|
|
jump_to_editor_ = app_basename.find("editor") != std::string::npos;
|
|
#endif
|
|
|
|
for(arg_ = 1; arg_ != argc_; ++arg_) {
|
|
const std::string val(argv_[arg_]);
|
|
if(val.empty()) {
|
|
continue;
|
|
}
|
|
|
|
if(val == "--fps") {
|
|
preferences::set_show_fps(true);
|
|
} else if(val == "--nocache") {
|
|
cache_.set_use_cache(false);
|
|
} else if(val == "--max-fps") {
|
|
if(arg_+1 != argc_) {
|
|
++arg_;
|
|
int fps = lexical_cast_default<int>(argv_[arg_], 50);
|
|
fps = std::min<int>(fps, 1000);
|
|
fps = std::max<int>(fps, 1);
|
|
fps = 1000 / fps;
|
|
// increase the delay to avoid going above the maximum
|
|
if(1000 % fps != 0) {
|
|
++fps;
|
|
}
|
|
preferences::set_draw_delay(fps);
|
|
}
|
|
} else if(val == "--validcache") {
|
|
cache_.set_force_valid_cache(true);
|
|
} else if(val == "--resolution" || val == "-r") {
|
|
if(arg_+1 != argc_) {
|
|
++arg_;
|
|
const std::string val(argv_[arg_]);
|
|
const std::vector<std::string> res = utils::split(val, 'x');
|
|
if(res.size() == 2) {
|
|
const int xres = lexical_cast_default<int>(res.front());
|
|
const int yres = lexical_cast_default<int>(res.back());
|
|
if(xres > 0 && yres > 0) {
|
|
const std::pair<int,int> resolution(xres,yres);
|
|
preferences::set_resolution(resolution);
|
|
}
|
|
}
|
|
}
|
|
} else if(val == "--bpp") {
|
|
if(arg_+1 != argc_) {
|
|
++arg_;
|
|
force_bpp_ = lexical_cast_default<int>(argv_[arg_],-1);
|
|
}
|
|
} else if(val == "--load" || val == "-l") {
|
|
if(arg_+1 != argc_) {
|
|
++arg_;
|
|
game::load_game_exception::game = argv_[arg_];
|
|
}
|
|
} else if(val == "--with-replay") {
|
|
game::load_game_exception::show_replay = true;
|
|
|
|
} else if(val == "--nogui") {
|
|
no_gui_ = true;
|
|
no_sound = true;
|
|
preferences::disable_preferences_save();
|
|
}
|
|
#ifndef DISABLE_EDITOR
|
|
else if(val == "--screenshot") {
|
|
if(arg_+2 != argc_) {
|
|
++arg_;
|
|
screenshot_map_ = argv_[arg_];
|
|
++arg_;
|
|
screenshot_filename_ = argv_[arg_];
|
|
no_sound = true;
|
|
screenshot_mode_ = true;
|
|
preferences::disable_preferences_save();
|
|
force_bpp_ = 32;
|
|
}
|
|
}
|
|
#endif
|
|
else if(val == "--smallgui") {
|
|
game_config::small_gui = true;
|
|
} else if(val == "--config-dir") {
|
|
if (argc_ <= ++arg_)
|
|
break;
|
|
} else if(val == "--windowed" || val == "-w") {
|
|
preferences::set_fullscreen(false);
|
|
} else if(val == "--fullscreen" || val == "-f") {
|
|
preferences::set_fullscreen(true);
|
|
|
|
} else if(val.find("--campaign") == 0 || val.find("-c") == 0) {
|
|
// campaign starting template:
|
|
// -c[[<difficulty>] <id_campaign> [<id_scenario>]]
|
|
// --campaign[[<difficulty>] <id_campaign> [<id_scenario>]]
|
|
jump_to_campaign_.jump_ = true;
|
|
|
|
// we don't know if the next argument is from --campaign
|
|
// or for setting the data directory, so we assume is the latter
|
|
if (arg_ + 2 < argc_ && argv_[arg_+1][0] != '-')
|
|
{
|
|
// we parse difficulty only here, since a campaign is supplied from command line
|
|
if (isdigit(val[val.size()-1]))
|
|
jump_to_campaign_.difficulty_ = lexical_cast<int>(val[val.size()-1]);
|
|
|
|
++arg_;
|
|
jump_to_campaign_.campaign_id_ = std::string(argv_[arg_]);
|
|
std::cerr<<"selected campaign id: ["<<jump_to_campaign_.campaign_id_
|
|
<<"] | difficulty: ["<<jump_to_campaign_.difficulty_<<"]\n";
|
|
}
|
|
|
|
if (arg_ + 2 < argc_ && argv_[arg_+1][0] != '-')
|
|
{
|
|
++arg_;
|
|
jump_to_campaign_.scenario_id_ = std::string(argv_[arg_]);
|
|
std::cerr<<"selected scenario id: ["<<jump_to_campaign_.scenario_id_<<"]\n";
|
|
}
|
|
} else if(val == "--server" || val == "-s"){
|
|
jump_to_multiplayer_ = true;
|
|
//Do we have any server specified ?
|
|
if(argc_ > arg_+1){
|
|
multiplayer_server_ = argv_[arg_+1];
|
|
++arg_;
|
|
//Pick the first server in config
|
|
}else{
|
|
if(game_config::server_list.size() > 0)
|
|
multiplayer_server_ = preferences::network_host();
|
|
else
|
|
multiplayer_server_ = "";
|
|
}
|
|
|
|
} else if(val == "--multiplayer" || val == "-m") {
|
|
multiplayer_mode_ = true;
|
|
break; //parse the rest of the arguments when we set up the game
|
|
} else if(val == "--test" || val == "-t") {
|
|
test_mode_ = true;
|
|
// If we have -t foo it's ambiguous whether it foo is the parameter
|
|
// for Wesnoth or the start directory so we assume it's the starting
|
|
// directory.
|
|
if(arg_ + 2 < argc_ && argv_[arg_ + 1][0] != '-') {
|
|
++arg_;
|
|
test_scenario_ = argv_[arg_];
|
|
}
|
|
} else if(val == "--debug" || val == "-d") {
|
|
game_config::debug = true;
|
|
game_config::mp_debug = true;
|
|
#ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
|
|
} else if (val.substr(0, 18) == "--debug-dot-level=") {
|
|
gui2::tdebug_layout_graph::set_level(val.substr(18));
|
|
} else if (val.substr(0, 19) == "--debug-dot-domain=") {
|
|
gui2::tdebug_layout_graph::set_domain(val.substr(19));
|
|
#endif
|
|
} else if(val == "--no-delay") {
|
|
game_config::no_delay = true;
|
|
} else if (val.substr(0, 6) == "--log-") {
|
|
} else if (val == "--rng-seed") {
|
|
++arg_;
|
|
} else if(val == "--nosound") {
|
|
no_sound = true;
|
|
} else if(val == "--nomusic") {
|
|
no_music = true;
|
|
} else if(val == "--new-storyscreens") {
|
|
// This is a hidden option to help testing
|
|
// the work-in-progress new storyscreen code.
|
|
// Don't document.
|
|
set_new_storyscreen(true);
|
|
} else if(val == "--new-widgets") {
|
|
// This is a hidden option to enable the new widget toolkit.
|
|
gui2::new_widgets = true;
|
|
#ifndef DISABLE_EDITOR
|
|
} else if(val == "-e" || val == "--editor") {
|
|
jump_to_editor_ = true;
|
|
if(arg_+1 != argc_) {
|
|
if (argv_[arg_ + 1][0] != '-') {
|
|
++arg_;
|
|
game::load_game_exception::game = argv_[arg_];
|
|
}
|
|
}
|
|
#endif
|
|
} else if(val[0] == '-') {
|
|
std::cerr << "unknown option: " << val << std::endl;
|
|
throw config::error("unknown option");
|
|
} else {
|
|
std::cerr << "Overriding data directory with " << val << std::endl;
|
|
#ifdef _WIN32
|
|
// use c_str to ensure that index 1 points to valid element since c_str() returns null-terminated string
|
|
if(val.c_str()[1] == ':') {
|
|
#else
|
|
if(val[0] == '/') {
|
|
#endif
|
|
game_config::path = val;
|
|
} else {
|
|
game_config::path = get_cwd() + '/' + val;
|
|
}
|
|
|
|
if(!is_directory(game_config::path)) {
|
|
std::cerr << "Could not find directory '" << game_config::path << "'\n";
|
|
throw config::error("directory not found");
|
|
}
|
|
|
|
font_manager_.update_font_path();
|
|
}
|
|
}
|
|
std::cerr << '\n';
|
|
std::cerr << "Data directory: " << game_config::path
|
|
<< "\nUser configuration directory: " << get_user_config_dir()
|
|
<< "\nUser data directory: " << get_user_data_dir()
|
|
<< "\nCache directory: " << get_cache_dir()
|
|
<< '\n';
|
|
|
|
// disable sound in nosound mode, or when sound engine failed to initialize
|
|
if (no_sound || ((preferences::sound_on() || preferences::music_on() ||
|
|
preferences::turn_bell() || preferences::UI_sound_on()) &&
|
|
!sound::init_sound())) {
|
|
preferences::set_sound(false);
|
|
preferences::set_music(false);
|
|
preferences::set_turn_bell(false);
|
|
preferences::set_UI_sound(false);
|
|
}
|
|
else if (no_music) { // else disable the music in nomusic mode
|
|
preferences::set_music(false);
|
|
}
|
|
}
|
|
|
|
game_display& game_controller::disp()
|
|
{
|
|
if(disp_.get() == NULL) {
|
|
if(get_video_surface() == NULL) {
|
|
throw CVideo::error();
|
|
}
|
|
disp_.assign(game_display::create_dummy_display(video_));
|
|
}
|
|
return *disp_.get();
|
|
}
|
|
|
|
bool game_controller::init_video()
|
|
{
|
|
if(no_gui_) {
|
|
if( !(multiplayer_mode_ || screenshot_mode_) ) {
|
|
std::cerr << "--nogui flag is only valid with --multiplayer flag or --screenshot flag\n";
|
|
return false;
|
|
}
|
|
video_.make_fake();
|
|
game_config::no_delay = true;
|
|
return true;
|
|
}
|
|
|
|
#if !(defined(__APPLE__))
|
|
surface icon(image::get_image("game-icon.png", image::UNSCALED));
|
|
if(icon != NULL) {
|
|
///must be called after SDL_Init() and before setting video mode
|
|
::SDL_WM_SetIcon(icon,NULL);
|
|
}
|
|
#endif
|
|
|
|
std::pair<int,int> resolution;
|
|
int bpp = 0;
|
|
int video_flags = 0;
|
|
|
|
bool found_matching = preferences::detect_video_settings(video_, resolution, bpp, video_flags);
|
|
|
|
if(force_bpp_ > 0) {
|
|
bpp = force_bpp_;
|
|
}
|
|
|
|
if(!found_matching) {
|
|
std::cerr << "Video mode " << resolution.first << 'x'
|
|
<< resolution.second << 'x' << bpp
|
|
<< " is not supported.\n";
|
|
|
|
if ((video_flags & FULL_SCREEN)) {
|
|
std::cerr << "Try running the program with the --windowed option "
|
|
<< "using a " << bpp << "bpp setting for your display adapter.\n";
|
|
} else {
|
|
std::cerr << "Try running the program with the --fullscreen option.\n";
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
std::cerr << "setting mode to " << resolution.first << "x" << resolution.second << "x" << bpp << "\n";
|
|
const int res = video_.setMode(resolution.first,resolution.second,bpp,video_flags);
|
|
video_.setBpp(bpp);
|
|
if(res == 0) {
|
|
std::cerr << "required video mode, " << resolution.first << "x"
|
|
<< resolution.second << "x" << bpp << " is not supported\n";
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool game_controller::init_config(const bool force)
|
|
{
|
|
cache_.clear_defines();
|
|
|
|
// make sure that multiplayer mode is set if command line parameter is selected
|
|
if (multiplayer_mode_)
|
|
cache_.add_define("MULTIPLAYER");
|
|
|
|
if (test_mode_)
|
|
cache_.add_define("TEST");
|
|
|
|
if (jump_to_editor_)
|
|
cache_.add_define("EDITOR");
|
|
|
|
if (!multiplayer_mode_ && !test_mode_ && !jump_to_editor_)
|
|
cache_.add_define("TITLE_SCREEN");
|
|
|
|
load_game_cfg(force);
|
|
|
|
const config &cfg = game_config().child("game_config");
|
|
game_config::load_config(cfg ? &cfg : NULL);
|
|
|
|
hotkey::deactivate_all_scopes();
|
|
hotkey::set_scope_active(hotkey::SCOPE_GENERAL);
|
|
hotkey::set_scope_active(hotkey::SCOPE_GAME);
|
|
|
|
hotkey::load_hotkeys(game_config());
|
|
paths_manager_.set_paths(game_config());
|
|
::init_textdomains(game_config());
|
|
about::set_about(game_config());
|
|
ai::configuration::init(game_config());
|
|
|
|
return true;
|
|
}
|
|
|
|
bool game_controller::init_language()
|
|
{
|
|
if(!::load_language_list())
|
|
return false;
|
|
|
|
if (!::set_language(get_locale()))
|
|
return false;
|
|
|
|
if(!no_gui_) {
|
|
std::string wm_title_string = _("The Battle for Wesnoth");
|
|
wm_title_string += " - " + game_config::revision;
|
|
SDL_WM_SetCaption(wm_title_string.c_str(), NULL);
|
|
}
|
|
|
|
hotkey::load_descriptions();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool game_controller::play_test()
|
|
{
|
|
static bool first_time = true;
|
|
|
|
if(test_mode_ == false) {
|
|
return true;
|
|
}
|
|
if(!first_time)
|
|
return false;
|
|
|
|
first_time = false;
|
|
|
|
state_.classification().campaign_type = "test";
|
|
state_.classification().scenario = test_scenario_;
|
|
state_.classification().campaign_define = "TEST";
|
|
cache_.add_define("TEST");
|
|
|
|
load_game_cfg();
|
|
|
|
paths_manager_.set_paths(game_config());
|
|
|
|
try {
|
|
upload_log nolog(false);
|
|
play_game(disp(),state_,game_config(),nolog);
|
|
} catch (game::load_game_exception &) {
|
|
test_mode_ = false;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool game_controller::play_screenshot_mode()
|
|
{
|
|
if(!screenshot_mode_) {
|
|
return true;
|
|
}
|
|
|
|
#ifndef DISABLE_EDITOR
|
|
cache_.clear_defines();
|
|
cache_.add_define("EDITOR");
|
|
load_game_cfg();
|
|
const binary_paths_manager bin_paths_manager(game_config());
|
|
::init_textdomains(game_config());
|
|
|
|
editor::start(game_config(), video_, screenshot_map_, true, screenshot_filename_);
|
|
return false;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool game_controller::play_multiplayer_mode()
|
|
{
|
|
state_ = game_state();
|
|
|
|
if(!multiplayer_mode_) {
|
|
return true;
|
|
}
|
|
|
|
std::string era = "era_default";
|
|
std::string scenario = "multiplayer_The_Freelands";
|
|
std::map<int,std::string> side_types, side_controllers, side_algorithms, side_ai_configs;
|
|
std::map<int,string_map> side_parameters;
|
|
std::string turns = "50";
|
|
std::string label = "";
|
|
|
|
size_t sides_counted = 0;
|
|
|
|
for(++arg_; arg_ < argc_; ++arg_) {
|
|
const std::string val(argv_[arg_]);
|
|
if(val.empty()) {
|
|
continue;
|
|
}
|
|
|
|
std::vector<std::string> name_value = utils::split(val, '=');
|
|
if(name_value.size() > 2) {
|
|
std::cerr << "invalid argument '" << val << "'\n";
|
|
return false;
|
|
} else if(name_value.size() == 2) {
|
|
const std::string name = name_value.front();
|
|
const std::string value = name_value.back();
|
|
|
|
const std::string name_head = name.substr(0,name.size()-1);
|
|
const char name_tail = name[name.size()-1];
|
|
const bool last_digit = isdigit(name_tail) ? true:false;
|
|
const size_t side = name_tail - '0';
|
|
|
|
if(last_digit && side > sides_counted) {
|
|
std::cerr << "counted sides: " << side << "\n";
|
|
sides_counted = side;
|
|
}
|
|
|
|
if(name == "--scenario") {
|
|
scenario = value;
|
|
} else if(name == "--turns") {
|
|
turns = value;
|
|
} else if(name == "--era") {
|
|
era = value;
|
|
} else if(name == "--label") {
|
|
label = value;
|
|
} else if(last_digit && name_head == "--controller") {
|
|
side_controllers[side] = value;
|
|
} else if(last_digit && name_head == "--ai_config") {
|
|
side_ai_configs[side] = value;
|
|
} else if(last_digit && name_head == "--algorithm") {
|
|
side_algorithms[side] = value;
|
|
} else if(last_digit && name_head == "--side") {
|
|
side_types[side] = value;
|
|
} else if(last_digit && name_head == "--parm") {
|
|
const std::vector<std::string> name_value = utils::split(value, ':');
|
|
if(name_value.size() != 2) {
|
|
std::cerr << "argument to '" << name << "' must be in the format name:value\n";
|
|
return false;
|
|
}
|
|
|
|
side_parameters[side][name_value.front()] = name_value.back();
|
|
} else {
|
|
std::cerr << "unrecognized option: '" << name << "'\n";
|
|
return false;
|
|
}
|
|
} else {
|
|
if (val == "--exit-at-end") {
|
|
game_config::exit_at_end = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
const config &lvl = game_config().find_child("multiplayer", "id", scenario);
|
|
if (!lvl) {
|
|
std::cerr << "Could not find scenario '" << scenario << "'\n";
|
|
return false;
|
|
}
|
|
|
|
state_.classification().campaign_type = "multiplayer";
|
|
state_.classification().scenario = "";
|
|
state_.snapshot = config();
|
|
|
|
config level = lvl;
|
|
std::vector<config*> story;
|
|
|
|
const config &era_cfg = game_config().find_child("era","id",era);
|
|
if (!era_cfg) {
|
|
std::cerr << "Could not find era '" << era << "'\n";
|
|
return false;
|
|
}
|
|
|
|
level["turns"] = turns;
|
|
|
|
const config &side = era_cfg.child("multiplayer_side");
|
|
if (!side) {
|
|
std::cerr << "Could not find multiplayer side\n";
|
|
return false;
|
|
}
|
|
|
|
while (level.child_count("side") < sides_counted) {
|
|
std::cerr << "now adding side...\n";
|
|
level.add_child("side");
|
|
}
|
|
|
|
int side_num = 1;
|
|
foreach (config &s, level.child_range("side"))
|
|
{
|
|
std::map<int,std::string>::const_iterator type = side_types.find(side_num),
|
|
controller = side_controllers.find(side_num),
|
|
algorithm = side_algorithms.find(side_num),
|
|
ai_config = side_ai_configs.find(side_num);
|
|
|
|
const config* side = type == side_types.end() ?
|
|
&era_cfg.find_child("multiplayer_side", "random_faction", "yes") :
|
|
&era_cfg.find_child("multiplayer_side", "id", type->second);
|
|
|
|
if (!*side) {
|
|
std::string side_name = (type == side_types.end() ? "default" : type->second);
|
|
std::cerr << "Could not find side '" << side_name << "' for side " << side_num << "\n";
|
|
return false;
|
|
}
|
|
|
|
if ((*side)["random_faction"].to_bool())
|
|
{
|
|
std::vector<std::string> faction_choices, faction_excepts;
|
|
faction_choices = utils::split((*side)["choices"]);
|
|
if(faction_choices.size() == 1 && faction_choices.front() == "") {
|
|
faction_choices.clear();
|
|
}
|
|
faction_excepts = utils::split((*side)["except"]);;
|
|
if(faction_excepts.size() == 1 && faction_excepts.front() == "") {
|
|
faction_excepts.clear();
|
|
}
|
|
unsigned j = 0;
|
|
foreach (const config &faction, era_cfg.child_range("multiplayer_side"))
|
|
{
|
|
if (faction["random_faction"].to_bool()) continue;
|
|
const std::string &faction_id = faction["id"];
|
|
if (!faction_choices.empty() &&
|
|
std::find(faction_choices.begin(), faction_choices.end(), faction_id) == faction_choices.end())
|
|
continue;
|
|
if (!faction_excepts.empty() &&
|
|
std::find(faction_excepts.begin(), faction_excepts.end(), faction_id) != faction_excepts.end())
|
|
continue;
|
|
if (rand() % ++j == 0)
|
|
side = &faction;
|
|
}
|
|
if ((*side)["random_faction"].to_bool()) {
|
|
std::string side_name = (type == side_types.end() ? "default" : type->second);
|
|
std::cerr << "Could not find any non-random faction for side " << side_num << "\n";
|
|
return false;
|
|
}
|
|
std::cerr << " Faction " << (*side)["name"] <<
|
|
" selected for side " << side_num << ".\n";
|
|
}
|
|
|
|
s["side"] = side_num;
|
|
s["canrecruit"] = true;
|
|
|
|
s.append(*side);
|
|
|
|
if(controller != side_controllers.end()) {
|
|
s["controller"] = controller->second;
|
|
}
|
|
|
|
if(algorithm != side_algorithms.end()) {
|
|
s["ai_algorithm"] = algorithm->second;
|
|
}
|
|
|
|
if(ai_config != side_ai_configs.end()) {
|
|
s["ai_config"] = ai_config->second;
|
|
}
|
|
|
|
config& ai_params = s.add_child("ai");
|
|
|
|
//now add in any arbitrary parameters given to the side
|
|
for(string_map::const_iterator j = side_parameters[side_num].begin(); j != side_parameters[side_num].end(); ++j) {
|
|
s[j->first] = j->second;
|
|
ai_params[j->first] = j->second;
|
|
}
|
|
++side_num;
|
|
}
|
|
level.add_child("era", era_cfg);
|
|
|
|
try {
|
|
//check if all sides are AI and we are using new uploader -> log these games
|
|
bool all_ai = true;
|
|
foreach (config &s, level.child_range("side")) {
|
|
if( s["controller"] != "ai" ) {
|
|
all_ai = false;
|
|
}
|
|
}
|
|
|
|
upload_log log( all_ai );
|
|
recorder.add_log_data("ai_log","ai_label",label);
|
|
|
|
state_.snapshot = level;
|
|
play_game(disp(), state_, game_config(), log);
|
|
} catch (game::load_game_exception &) {
|
|
//the user's trying to load a game, so go into the normal title screen loop and load one
|
|
return true;
|
|
} catch(twml_exception& e) {
|
|
e.show(disp());
|
|
return false;
|
|
} catch(std::exception& e) {
|
|
std::cerr << "caught exception: " << e.what() << "\n";
|
|
} catch(...) {
|
|
std::cerr << "caught unknown error playing level...\n";
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool game_controller::is_loading() const
|
|
{
|
|
return !game::load_game_exception::game.empty();
|
|
}
|
|
|
|
bool game_controller::load_game()
|
|
{
|
|
savegame::loadgame load(disp(), game_config(), state_);
|
|
|
|
try {
|
|
load.load_game(game::load_game_exception::game, game::load_game_exception::show_replay, game::load_game_exception::cancel_orders);
|
|
|
|
cache_.clear_defines();
|
|
game_config::scoped_preproc_define dificulty_def(state_.classification().difficulty);
|
|
|
|
game_config::scoped_preproc_define campaign_define_def(state_.classification().campaign_define, !state_.classification().campaign_define.empty());
|
|
|
|
game_config::scoped_preproc_define campaign_type_def("MULTIPLAYER", state_.classification().campaign_define.empty() && (state_.classification().campaign_type == "multiplayer"));
|
|
|
|
typedef boost::shared_ptr<game_config::scoped_preproc_define> define_ptr;
|
|
std::deque<define_ptr> extra_defines;
|
|
for(std::vector<std::string>::const_iterator i = state_.classification().campaign_xtra_defines.begin(); i != state_.classification().campaign_xtra_defines.end(); ++i) {
|
|
define_ptr newdefine(new game_config::scoped_preproc_define(*i));
|
|
extra_defines.push_back(newdefine);
|
|
}
|
|
|
|
try {
|
|
load_game_cfg();
|
|
} catch(config::error&) {
|
|
cache_.clear_defines();
|
|
load_game_cfg();
|
|
return false;
|
|
}
|
|
|
|
paths_manager_.set_paths(game_config());
|
|
load.set_gamestate();
|
|
|
|
} catch(load_game_cancelled_exception&) {
|
|
clear_loaded_game();
|
|
return false;
|
|
} catch(config::error& e) {
|
|
if(e.message.empty()) {
|
|
gui2::show_error_message(disp().video(), _("The file you have tried to load is corrupt"));
|
|
}
|
|
else {
|
|
gui2::show_error_message(disp().video(), _("The file you have tried to load is corrupt: '") + e.message + '\'');
|
|
}
|
|
return false;
|
|
} catch(twml_exception& e) {
|
|
e.show(disp());
|
|
return false;
|
|
} catch(io_exception& e) {
|
|
if(e.message.empty()) {
|
|
gui2::show_error_message(disp().video(), _("File I/O Error while reading the game"));
|
|
} else {
|
|
gui2::show_error_message(disp().video(), _("File I/O Error while reading the game: '") + e.message + '\'');
|
|
}
|
|
return false;
|
|
} catch(game::error& e) {
|
|
if(e.message.empty()) {
|
|
gui2::show_error_message(disp().video(), _("The file you have tried to load is corrupt"));
|
|
}
|
|
else {
|
|
gui2::show_error_message(disp().video(), _("The file you have tried to load is corrupt: '") + e.message + '\'');
|
|
}
|
|
return false;
|
|
}
|
|
recorder = replay(state_.replay_data);
|
|
recorder.start_replay();
|
|
recorder.set_skip(false);
|
|
|
|
LOG_CONFIG << "has snapshot: " << (state_.snapshot.child("side") ? "yes" : "no") << "\n";
|
|
|
|
if (!state_.snapshot.child("side")) {
|
|
// No snapshot; this is a start-of-scenario
|
|
if (load.show_replay()) {
|
|
// There won't be any turns to replay, but the
|
|
// user gets to watch the intro sequence again ...
|
|
LOG_CONFIG << "replaying (start of scenario)\n";
|
|
} else {
|
|
LOG_CONFIG << "skipping...\n";
|
|
recorder.set_skip(false);
|
|
}
|
|
} else {
|
|
// We have a snapshot. But does the user want to see a replay?
|
|
if(load.show_replay()) {
|
|
statistics::clear_current_scenario();
|
|
LOG_CONFIG << "replaying (snapshot)\n";
|
|
} else {
|
|
LOG_CONFIG << "setting replay to end...\n";
|
|
recorder.set_to_end();
|
|
if(!recorder.at_end()) {
|
|
WRN_CONFIG << "recorder is not at the end!!!\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
if(state_.classification().campaign_type == "multiplayer") {
|
|
foreach (config &side, state_.snapshot.child_range("side"))
|
|
{
|
|
if (side["controller"] == "network")
|
|
side["controller"] = "human";
|
|
if (side["controller"] == "network_ai")
|
|
side["controller"] = "human_ai";
|
|
}
|
|
}
|
|
|
|
if (load.cancel_orders()) {
|
|
foreach (config &side, state_.snapshot.child_range("side"))
|
|
{
|
|
if (side["controller"] != "human") continue;
|
|
foreach (config &unit, side.child_range("unit"))
|
|
{
|
|
unit["goto_x"] = -999;
|
|
unit["goto_y"] = -999;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void game_controller::set_tutorial()
|
|
{
|
|
state_ = game_state();
|
|
state_.classification().campaign_type = "tutorial";
|
|
state_.classification().scenario = "tutorial";
|
|
state_.classification().campaign_define = "TUTORIAL";
|
|
cache_.clear_defines();
|
|
cache_.add_define("TUTORIAL");
|
|
|
|
}
|
|
|
|
void game_controller::mark_completed_campaigns(std::vector<config> &campaigns)
|
|
{
|
|
foreach (config &campaign, campaigns) {
|
|
campaign["completed"] = preferences::is_campaign_completed(campaign["id"]);
|
|
}
|
|
}
|
|
|
|
bool game_controller::new_campaign()
|
|
{
|
|
state_ = game_state();
|
|
state_.classification().campaign_type = "scenario";
|
|
|
|
const config::const_child_itors &ci = game_config().child_range("campaign");
|
|
std::vector<config> campaigns(ci.first, ci.second);
|
|
mark_completed_campaigns(campaigns);
|
|
std::sort(campaigns.begin(),campaigns.end(),less_campaigns_rank);
|
|
|
|
|
|
if(campaigns.begin() == campaigns.end()) {
|
|
gui2::show_error_message(disp().video(),
|
|
_("No campaigns are available.\n"));
|
|
return false;
|
|
}
|
|
|
|
int campaign_num = -1;
|
|
// No campaign selected from command line
|
|
if (jump_to_campaign_.campaign_id_.empty() == true)
|
|
{
|
|
gui2::tcampaign_selection dlg(campaigns);
|
|
|
|
try {
|
|
dlg.show(disp().video());
|
|
} catch(twml_exception& e) {
|
|
e.show(disp());
|
|
return false;
|
|
}
|
|
|
|
if(dlg.get_retval() != gui2::twindow::OK) {
|
|
return false;
|
|
}
|
|
|
|
campaign_num = dlg.get_choice();
|
|
}
|
|
else
|
|
{
|
|
// don't reset the campaign_id_ so we can know
|
|
// if we should quit the game or return to the main menu
|
|
|
|
// checking for valid campaign name
|
|
for(size_t i = 0; i < campaigns.size(); ++i)
|
|
{
|
|
if (campaigns[i]["id"] == jump_to_campaign_.campaign_id_)
|
|
{
|
|
campaign_num = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// didn't found any campaign with that id
|
|
if (campaign_num == -1)
|
|
{
|
|
std::cerr<<"No such campaign id to jump to: ["<<jump_to_campaign_.campaign_id_<<"]\n";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const config &campaign = campaigns[campaign_num];
|
|
state_.classification().campaign = campaign["id"].str();
|
|
state_.classification().abbrev = campaign["abbrev"].str();
|
|
|
|
// we didn't specify in the command line the scenario to be started
|
|
if (jump_to_campaign_.scenario_id_.empty())
|
|
state_.classification().scenario = campaign["first_scenario"].str();
|
|
else
|
|
{
|
|
config scenario;
|
|
foreach(const config &sc, campaign.child_range("scenario"))
|
|
{
|
|
if (sc["id"] == jump_to_campaign_.scenario_id_)
|
|
scenario = sc;
|
|
}
|
|
|
|
if (scenario == NULL)
|
|
{
|
|
std::cerr<<"No such scenario id to jump to: ["<<jump_to_campaign_.scenario_id_<<"]\n";
|
|
return false;
|
|
}
|
|
state_.classification().scenario = jump_to_campaign_.scenario_id_;
|
|
}
|
|
state_.classification().end_text = campaign["end_text"].str();
|
|
state_.classification().end_text_duration = campaign["end_text_duration"];
|
|
|
|
const std::string difficulty_descriptions = campaign["difficulty_descriptions"];
|
|
std::vector<std::string> difficulty_options = utils::split(difficulty_descriptions, ';');
|
|
|
|
const std::vector<std::string> difficulties = utils::split(campaign["difficulties"]);
|
|
|
|
if(difficulties.empty() == false) {
|
|
int difficulty = 0;
|
|
if (jump_to_campaign_.difficulty_ == -1){
|
|
if(difficulty_options.size() != difficulties.size()) {
|
|
difficulty_options.resize(difficulties.size());
|
|
std::copy(difficulties.begin(),difficulties.end(),difficulty_options.begin());
|
|
}
|
|
|
|
gui::dialog dlg(disp(), _("Difficulty"),
|
|
_("Select difficulty level:"), gui::OK_CANCEL);
|
|
dlg.set_menu(difficulty_options);
|
|
if(dlg.show() == -1) {
|
|
if (jump_to_campaign_.campaign_id_.empty() == false)
|
|
{
|
|
jump_to_campaign_.campaign_id_ = "";
|
|
}
|
|
// canceled difficulty dialog, relaunch the campaign selection dialog
|
|
return new_campaign();
|
|
}
|
|
difficulty = dlg.result();
|
|
}
|
|
else
|
|
{
|
|
if (jump_to_campaign_.difficulty_
|
|
> static_cast<int>(difficulties.size()))
|
|
{
|
|
std::cerr << "incorrect difficulty number: [" <<
|
|
jump_to_campaign_.difficulty_ << "]. maximum is [" <<
|
|
difficulties.size() << "].\n";
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
difficulty = jump_to_campaign_.difficulty_ - 1;
|
|
}
|
|
}
|
|
|
|
state_.classification().difficulty = difficulties[difficulty];
|
|
cache_.clear_defines();
|
|
cache_.add_define(difficulties[difficulty]);
|
|
} else {
|
|
//clear even when there is no difficulty
|
|
cache_.clear_defines();
|
|
}
|
|
|
|
state_.classification().campaign_define = campaign["define"].str();
|
|
state_.classification().campaign_xtra_defines = utils::split(campaign["extra_defines"]);
|
|
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
std::string game_controller::jump_to_campaign_id() const
|
|
{
|
|
return jump_to_campaign_.campaign_id_;
|
|
}
|
|
|
|
bool game_controller::goto_campaign()
|
|
{
|
|
if(jump_to_campaign_.jump_){
|
|
if(new_campaign()) {
|
|
jump_to_campaign_.jump_ = false;
|
|
launch_game(game_controller::RELOAD_DATA);
|
|
}else{
|
|
jump_to_campaign_.jump_ = false;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool game_controller::goto_multiplayer()
|
|
{
|
|
if(jump_to_multiplayer_){
|
|
jump_to_multiplayer_ = false;
|
|
if(play_multiplayer()){
|
|
;
|
|
}else{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#ifndef DISABLE_EDITOR
|
|
bool game_controller::goto_editor()
|
|
{
|
|
if(jump_to_editor_){
|
|
jump_to_editor_ = false;
|
|
if (start_editor(normalize_path(game::load_game_exception::game)) ==
|
|
editor::EXIT_QUIT_TO_DESKTOP)
|
|
{
|
|
return false;
|
|
}
|
|
clear_loaded_game();
|
|
}
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
namespace
|
|
{
|
|
void game_controller::reload_changed_game_config()
|
|
{
|
|
// rebuild addon version info cache
|
|
refresh_addon_version_info_cache();
|
|
|
|
//force a reload of configuration information
|
|
cache_.recheck_filetree_checksum();
|
|
old_defines_map_.clear();
|
|
clear_binary_paths_cache();
|
|
init_config(true);
|
|
}
|
|
|
|
void game_controller::start_wesnothd()
|
|
{
|
|
const std::string wesnothd_program =
|
|
preferences::get_mp_server_program_name().empty() ?
|
|
get_program_invocation("wesnothd") : preferences::get_mp_server_program_name();
|
|
|
|
std::string config = get_user_config_dir() + "/lan_server.cfg";
|
|
if (!file_exists(config)) {
|
|
// copy file if it isn't created yet
|
|
write_file(config, read_file(get_wml_location("lan_server.cfg")));
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
std::string command = "\"" + wesnothd_program +"\" -c \"" + config + "\" -d -t 2 -T 5";
|
|
#else
|
|
// start wesnoth as background job
|
|
std::string command = "cmd /C start \"wesnoth server\" /B \"" + wesnothd_program + "\" -c \"" + config + "\" -t 2 -T 5";
|
|
#endif
|
|
LOG_GENERAL << "Starting wesnothd: "<< command << "\n";
|
|
if (std::system(command.c_str()) == 0) {
|
|
// Give server a moment to start up
|
|
SDL_Delay(50);
|
|
return;
|
|
}
|
|
preferences::set_mp_server_program_name("");
|
|
|
|
// Couldn't start server so throw error
|
|
WRN_GENERAL << "Failed to run server start script\n";
|
|
throw game::mp_server_error("Starting MP server failed!");
|
|
}
|
|
|
|
bool game_controller::play_multiplayer()
|
|
{
|
|
int res;
|
|
|
|
state_ = game_state();
|
|
state_.classification().campaign_type = "multiplayer";
|
|
state_.classification().campaign_define = "MULTIPLAYER";
|
|
|
|
//Print Gui only if the user hasn't specified any server
|
|
if( multiplayer_server_.empty() ){
|
|
|
|
int start_server;
|
|
do {
|
|
start_server = 0;
|
|
|
|
gui2::tmp_method_selection dlg;
|
|
|
|
dlg.show(disp().video());
|
|
|
|
if(dlg.get_retval() == gui2::twindow::OK) {
|
|
res = dlg.get_choice();
|
|
} else {
|
|
return false;
|
|
|
|
}
|
|
|
|
if (res == 2 && preferences::mp_server_warning_disabled() < 2)
|
|
{
|
|
gui::dialog d(disp(), _("Do you really want to start the server?"),
|
|
_("The server will run in a background process until all users have disconnected.")
|
|
, gui::OK_CANCEL);
|
|
bool checked = preferences::mp_server_warning_disabled() != 1;
|
|
|
|
d.add_option(_("Don't show again"), checked, gui::dialog::BUTTON_CHECKBOX_LEFT);
|
|
start_server = d.show();
|
|
if (start_server == 0)
|
|
preferences::set_mp_server_warning_disabled(d.option_checked()?2:1);
|
|
|
|
}
|
|
} while (start_server);
|
|
if (res < 0) {
|
|
return false;
|
|
}
|
|
|
|
}else{
|
|
res = 4;
|
|
}
|
|
|
|
try {
|
|
if (res == 2)
|
|
{
|
|
try {
|
|
start_wesnothd();
|
|
} catch(game::mp_server_error&)
|
|
{
|
|
std::string path = preferences::show_wesnothd_server_search(disp());
|
|
|
|
if (!path.empty())
|
|
{
|
|
preferences::set_mp_server_program_name(path);
|
|
start_wesnothd();
|
|
}
|
|
else
|
|
{
|
|
throw game::mp_server_error("No path given for mp server program.");
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
/* do */ {
|
|
cache_.clear_defines();
|
|
game_config::scoped_preproc_define multiplayer(state_.classification().campaign_define);
|
|
load_game_cfg();
|
|
events::discard(INPUT_MASK); // prevent the "keylogger" effect
|
|
cursor::set(cursor::NORMAL);
|
|
// update binary paths
|
|
paths_manager_.set_paths(game_config());
|
|
clear_binary_paths_cache();
|
|
}
|
|
|
|
if(res == 3) {
|
|
std::vector<std::string> chat;
|
|
config game_data;
|
|
|
|
const mp::controller cntr = mp::CNTR_LOCAL;
|
|
|
|
mp::start_local_game(disp(), game_config(), cntr);
|
|
|
|
} else if((res >= 0 && res <= 2) || res == 4) {
|
|
std::string host;
|
|
if(res == 0) {
|
|
host = preferences::server_list().front().address;
|
|
}else if(res == 2) {
|
|
host = "localhost";
|
|
}else if(res == 4){
|
|
host = multiplayer_server_;
|
|
multiplayer_server_ = "";
|
|
}
|
|
mp::start_client(disp(), game_config(), host);
|
|
}
|
|
|
|
} catch(game::mp_server_error& e) {
|
|
gui2::show_error_message(disp().video(), _("Error while starting server: ") + e.message);
|
|
} catch(game::load_game_failed& e) {
|
|
gui2::show_error_message(disp().video(), _("The game could not be loaded: ") + e.message);
|
|
} catch(game::game_error& e) {
|
|
gui2::show_error_message(disp().video(), _("Error while playing the game: ") + e.message);
|
|
} catch(network::error& e) {
|
|
if(e.message != "") {
|
|
ERR_NET << "caught network::error: " << e.message << "\n";
|
|
gui2::show_transient_message(disp().video()
|
|
, ""
|
|
, gettext(e.message.c_str()));
|
|
} else {
|
|
ERR_NET << "caught network::error\n";
|
|
}
|
|
} catch(config::error& e) {
|
|
if(e.message != "") {
|
|
ERR_CONFIG << "caught config::error: " << e.message << "\n";
|
|
gui2::show_transient_message(disp().video(), "", e.message);
|
|
} else {
|
|
ERR_CONFIG << "caught config::error\n";
|
|
}
|
|
} catch(incorrect_map_format_error& e) {
|
|
gui2::show_error_message(disp().video(), std::string(_("The game map could not be loaded: ")) + e.message);
|
|
} catch (game::load_game_exception &) {
|
|
//this will make it so next time through the title screen loop, this game is loaded
|
|
} catch(twml_exception& e) {
|
|
e.show(disp());
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool game_controller::change_language()
|
|
{
|
|
gui2::tlanguage_selection dlg;
|
|
dlg.show(disp().video());
|
|
if (dlg.get_retval() != gui2::twindow::OK) return false;
|
|
|
|
if (!no_gui_) {
|
|
std::string wm_title_string = _("The Battle for Wesnoth");
|
|
wm_title_string += " - " + game_config::revision;
|
|
SDL_WM_SetCaption(wm_title_string.c_str(), NULL);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void game_controller::show_preferences()
|
|
{
|
|
const preferences::display_manager disp_manager(&disp());
|
|
preferences::show_preferences_dialog(disp(),game_config());
|
|
|
|
disp().redraw_everything();
|
|
}
|
|
|
|
void game_controller::show_upload_begging()
|
|
{
|
|
upload_log_dialog::show_beg_dialog(disp());
|
|
|
|
disp().redraw_everything();
|
|
}
|
|
|
|
|
|
void game_controller::set_unit_data()
|
|
{
|
|
if (config &units = game_config_.child("units")) {
|
|
unit_types.set_config(units);
|
|
}
|
|
}
|
|
|
|
void game_controller::load_game_cfg(const bool force)
|
|
{
|
|
// make sure that 'debug mode' symbol is set if command line parameter is selected
|
|
// also if we're in multiplayer and actual debug mode is disabled
|
|
if (game_config::debug || game_config::mp_debug) {
|
|
cache_.add_define("DEBUG_MODE");
|
|
}
|
|
|
|
gui::set_background_dirty();
|
|
if (!game_config_.empty() && !force
|
|
&& old_defines_map_ == cache_.get_preproc_map())
|
|
return; // game_config already holds requested config in memory
|
|
old_defines_map_ = cache_.get_preproc_map();
|
|
loadscreen::global_loadscreen_manager loadscreen_manager(disp().video());
|
|
cursor::setter cur(cursor::WAIT);
|
|
// The loadscreen will erase the titlescreen
|
|
// NOTE: even without loadscreen, needed after MP lobby
|
|
try {
|
|
/**
|
|
* Read all game configs
|
|
* First we should load data/
|
|
* Then handle terrains so that they are last loaded from data/
|
|
* 2nd everything in userdata
|
|
**/
|
|
//reset the parse counter before reading the game files
|
|
data_tree_checksum();
|
|
loadscreen::global_loadscreen->parser_counter = 0;
|
|
|
|
// start transaction so macros are shared
|
|
game_config::config_cache_transaction main_transaction;
|
|
|
|
cache_.get_config(game_config::path +"/data", game_config_);
|
|
|
|
main_transaction.lock();
|
|
|
|
// clone and put the gfx rules aside so that we can prepend the add-on
|
|
// rules to them.
|
|
config core_terrain_rules;
|
|
// FIXME: there should be a canned algorithm for cloning child_list objects,
|
|
// along with the memory their elements point to... little implementation detail.
|
|
foreach (config const &p_cfg, game_config_.child_range("terrain_graphics")) {
|
|
core_terrain_rules.add_child("terrain_graphics", p_cfg);
|
|
}
|
|
game_config_.clear_children("terrain_graphics");
|
|
|
|
// load usermade add-ons
|
|
const std::string user_campaign_dir = get_addon_campaigns_dir();
|
|
std::vector< std::string > error_addons;
|
|
// Scan addon directories
|
|
std::vector<std::string> user_dirs;
|
|
// Scan for standalone files
|
|
std::vector<std::string> user_files;
|
|
|
|
// The addons that we'll actually load
|
|
std::vector<std::string> addons_to_load;
|
|
|
|
get_files_in_dir(user_campaign_dir,&user_files,&user_dirs,ENTIRE_FILE_PATH);
|
|
std::string user_error_log;
|
|
|
|
// Append the $user_campaign_dir/*.cfg files to addons_to_load.
|
|
for(std::vector<std::string>::const_iterator uc = user_files.begin(); uc != user_files.end(); ++uc) {
|
|
const std::string file = *uc;
|
|
if(file.substr(file.size() - 4, file.size()) == ".cfg")
|
|
addons_to_load.push_back(file);
|
|
}
|
|
|
|
// Append the $user_campaign_dir/*/_main.cfg files to addons_to_load.
|
|
for(std::vector<std::string>::const_iterator uc = user_dirs.begin(); uc != user_dirs.end(); ++uc){
|
|
const std::string main_cfg = *uc + "/_main.cfg";
|
|
if (file_exists(main_cfg))
|
|
addons_to_load.push_back(main_cfg);
|
|
}
|
|
|
|
// Load the addons
|
|
for(std::vector<std::string>::const_iterator uc = addons_to_load.begin(); uc != addons_to_load.end(); ++uc) {
|
|
const std::string toplevel = *uc;
|
|
try {
|
|
config umc_cfg;
|
|
cache_.get_config(toplevel, umc_cfg);
|
|
|
|
game_config_.append(umc_cfg);
|
|
} catch(config::error& err) {
|
|
ERR_CONFIG << "error reading usermade add-on '" << *uc << "'\n";
|
|
error_addons.push_back(*uc);
|
|
user_error_log += err.message + "\n";
|
|
} catch(preproc_config::error& err) {
|
|
ERR_CONFIG << "error reading usermade add-on '" << *uc << "'\n";
|
|
error_addons.push_back(*uc);
|
|
user_error_log += err.message + "\n";
|
|
} catch(io_exception&) {
|
|
ERR_CONFIG << "error reading usermade add-on '" << *uc << "'\n";
|
|
error_addons.push_back(*uc);
|
|
}
|
|
if(error_addons.empty() == false) {
|
|
std::stringstream msg;
|
|
msg << _n("The following add-on had errors and could not be loaded:",
|
|
"The following add-ons had errors and could not be loaded:",
|
|
error_addons.size());
|
|
for(std::vector<std::string>::const_iterator i = error_addons.begin(); i != error_addons.end(); ++i) {
|
|
msg << "\n" << *i;
|
|
}
|
|
|
|
msg << '\n' << _("ERROR DETAILS:") << '\n' << user_error_log;
|
|
|
|
gui2::show_error_message(disp().video(),msg.str());
|
|
}
|
|
}
|
|
|
|
// Extract the Lua scripts at toplevel.
|
|
extract_preload_scripts(game_config_);
|
|
game_config_.clear_children("lua");
|
|
|
|
game_config_.merge_children("units");
|
|
game_config_.append(core_terrain_rules);
|
|
|
|
config& hashes = game_config_.add_child("multiplayer_hashes");
|
|
foreach (const config &ch, game_config_.child_range("multiplayer")) {
|
|
hashes[ch["id"]] = ch.hash();
|
|
}
|
|
|
|
set_unit_data();
|
|
|
|
terrain_builder::set_terrain_rules_cfg(game_config());
|
|
|
|
} catch(game::error& e) {
|
|
ERR_CONFIG << "Error loading game configuration files\n";
|
|
gui2::show_error_message(disp().video(), _("Error loading game configuration files: '") +
|
|
e.message + _("' (The game will now exit)"));
|
|
throw;
|
|
}
|
|
}
|
|
|
|
|
|
void game_controller::launch_game(RELOAD_GAME_DATA reload)
|
|
{
|
|
loadscreen::global_loadscreen_manager loadscreen_manager(disp().video());
|
|
loadscreen::global_loadscreen->set_progress(0, _("Loading data files"));
|
|
if(reload == RELOAD_DATA) {
|
|
game_config::scoped_preproc_define campaign_define(state_.classification().campaign_define, state_.classification().campaign_define.empty() == false);
|
|
|
|
typedef boost::shared_ptr<game_config::scoped_preproc_define> define_ptr;
|
|
std::deque<define_ptr> extra_defines;
|
|
for(std::vector<std::string>::const_iterator i = state_.classification().campaign_xtra_defines.begin(); i != state_.classification().campaign_xtra_defines.end(); ++i) {
|
|
define_ptr newdefine(new game_config::scoped_preproc_define(*i));
|
|
extra_defines.push_back(newdefine);
|
|
}
|
|
try {
|
|
load_game_cfg();
|
|
} catch(config::error&) {
|
|
cache_.clear_defines();
|
|
load_game_cfg();
|
|
return;
|
|
}
|
|
}
|
|
|
|
loadscreen::global_loadscreen->set_progress(60);
|
|
|
|
const binary_paths_manager bin_paths_manager(game_config());
|
|
|
|
try {
|
|
// Only record log for single-player games & tutorial.
|
|
upload_log log(state_.classification().campaign_type.empty()
|
|
|| state_.classification().campaign_type == "scenario"
|
|
|| state_.classification().campaign_type == "tutorial");
|
|
|
|
const LEVEL_RESULT result = play_game(disp(),state_,game_config(), log);
|
|
// don't show The End for multiplayer scenario
|
|
// change this if MP campaigns are implemented
|
|
if(result == VICTORY && (state_.classification().campaign_type.empty() || state_.classification().campaign_type != "multiplayer")) {
|
|
preferences::add_completed_campaign(state_.classification().campaign);
|
|
the_end(disp(), state_.classification().end_text, state_.classification().end_text_duration);
|
|
about::show_about(disp(),state_.classification().campaign);
|
|
}
|
|
|
|
clear_loaded_game();
|
|
} catch (game::load_game_exception &) {
|
|
//this will make it so next time through the title screen loop, this game is loaded
|
|
} catch(twml_exception& e) {
|
|
e.show(disp());
|
|
}
|
|
}
|
|
|
|
} //end anon namespace
|
|
|
|
void game_controller::play_replay()
|
|
{
|
|
const binary_paths_manager bin_paths_manager(game_config());
|
|
|
|
try {
|
|
::play_replay(disp(),state_,game_config(),video_);
|
|
|
|
clear_loaded_game();
|
|
} catch (game::load_game_exception &) {
|
|
//this will make it so next time through the title screen loop, this game is loaded
|
|
} catch(twml_exception& e) {
|
|
e.show(disp());
|
|
}
|
|
}
|
|
|
|
#ifndef DISABLE_EDITOR
|
|
editor::EXIT_STATUS game_controller::start_editor(const std::string& filename)
|
|
{
|
|
cache_.clear_defines();
|
|
cache_.add_define("EDITOR");
|
|
load_game_cfg();
|
|
const binary_paths_manager bin_paths_manager(game_config());
|
|
::init_textdomains(game_config());
|
|
return editor::start(game_config(), video_, filename);
|
|
}
|
|
#endif
|
|
|
|
game_controller::~game_controller()
|
|
{
|
|
delete gui::empty_menu;
|
|
sound::close_sound();
|
|
}
|
|
|
|
// this is needed to allow identical functionality with clean refactoring
|
|
// play_game only returns on an error, all returns within play_game can
|
|
// be replaced with this
|
|
static void safe_exit(int res) {
|
|
|
|
LOG_GENERAL << "exiting with code " << res << "\n";
|
|
#ifdef OS2 /* required to correctly shutdown SDL on OS/2 */
|
|
SDL_Quit();
|
|
#endif
|
|
exit(res);
|
|
}
|
|
|
|
// maybe this should go in a util file somewhere?
|
|
static void gzip_codec(const std::string & input_file, const std::string & output_file, bool encode)
|
|
{
|
|
try {
|
|
std::ofstream ofile(output_file.c_str(), std::ios_base::out
|
|
| std::ios_base::binary | std::ios_base::binary);
|
|
std::ifstream ifile(input_file.c_str(),
|
|
std::ios_base::in | std::ios_base::binary);
|
|
boost::iostreams::filtering_streambuf<boost::iostreams::input> in;
|
|
if(encode)
|
|
in.push(boost::iostreams::gzip_compressor());
|
|
else
|
|
in.push(boost::iostreams::gzip_decompressor());
|
|
in.push(ifile);
|
|
boost::iostreams::copy(in, ofile);
|
|
ifile.close();
|
|
safe_exit(remove(input_file.c_str()));
|
|
} catch(io_exception& e) {
|
|
std::cerr << "IO error: " << e.what() << "\n";
|
|
}
|
|
}
|
|
|
|
static void gzip_encode(const std::string & input_file, const std::string & output_file)
|
|
{
|
|
gzip_codec(input_file, output_file, true);
|
|
}
|
|
|
|
static void gzip_decode(const std::string & input_file, const std::string & output_file)
|
|
{
|
|
gzip_codec(input_file, output_file, false);
|
|
}
|
|
|
|
struct preprocess_options
|
|
{
|
|
public:
|
|
preprocess_options(): output_macros_path_("false"), input_macros_()
|
|
{
|
|
}
|
|
std::string output_macros_path_;
|
|
preproc_map input_macros_;
|
|
};
|
|
|
|
/** Process commandline-arguments */
|
|
static int process_command_args(int argc, char** argv) {
|
|
const std::string program = argv[0];
|
|
game_config::wesnoth_program_dir = directory_name(program);
|
|
preprocess_options preproc;
|
|
|
|
//parse arguments that shouldn't require a display device
|
|
int arg;
|
|
for(arg = 1; arg != argc; ++arg) {
|
|
const std::string val(argv[arg]);
|
|
if(val.empty()) {
|
|
continue;
|
|
}
|
|
|
|
if(val == "--help" || val == "-h") {
|
|
// When adding items don't forget to update doc/man/wesnoth.6
|
|
// Options are sorted alphabetically by --long-option.
|
|
// Please keep the output to 80 chars per line.
|
|
std::cout << "usage: " << argv[0]
|
|
<< " [<options>] [<data-directory>]\n"
|
|
<< "Available options:\n"
|
|
<< " --bpp <number> sets BitsPerPixel value. Example: --bpp 32\n"
|
|
<< " -c, --campaign[[<difficulty>] <id_c> [<id_s>]]\n"
|
|
<< " goes directly to the campaign.\n"
|
|
<< " - difficulty : the difficulty of the specified\n"
|
|
<< " campaign (1 to max - Default is 1)\n"
|
|
<< " - id_c: the id of the campaign. A selection \n"
|
|
<< " menu will appear if none specified\n"
|
|
<< " - id_s: the id of the scenario from the\n"
|
|
<< " specified campaign\n"
|
|
<< " --config-dir <name> sets the path of the user config directory to\n"
|
|
<< " $HOME/<name> or My Documents\\My Games\\<name> for windows.\n"
|
|
<< " You can specify also an absolute path outside the\n"
|
|
<< " $HOME or My Documents\\My Games directory."
|
|
<< " --config-path prints the path of the user config directory and\n"
|
|
<< " exits.\n"
|
|
<< " --data-dir <directory> overrides the data directory with the one specified.\n"
|
|
<< " -d, --debug enables additional command mode options in-game.\n"
|
|
#ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
|
|
<< " --debug-dot-level=<level1>,<level2>,...\n"
|
|
<< " sets the level of the debug dot files.\n"
|
|
<< " These files are used for debugging the widgets\n"
|
|
<< " especially the for the layout engine. When enabled\n"
|
|
<< " the engine will produce dot files which can be\n"
|
|
<< " converted to images with the dot tool.\n"
|
|
<< " Available levels:\n"
|
|
<< " - size : generate the size info of the widget.\n"
|
|
<< " - state : generate the state info of the widget.\n"
|
|
<< " --debug-dot-domain=<domain1>,<domain2>,...\n"
|
|
<< " sets the domain of the debug dot files.\n"
|
|
<< " see --debug-dot-level for more info.\n"
|
|
<< " Available domains:\n"
|
|
<< " show : generate the data when the dialog is\n"
|
|
<< " about to be shown.\n"
|
|
<< " layout : generate the data during the layout\n"
|
|
<< " phase (might result in multiple files. \n"
|
|
<< " The data can also be generated when the F12 is\n"
|
|
<< " pressed in a dialog.\n"
|
|
#endif
|
|
#ifndef DISABLE_EDITOR
|
|
<< " -e, --editor [<file>] starts the in-game map editor directly. If <file>\n"
|
|
<< " is specified, equivalent to -e --load <file>.\n"
|
|
#endif
|
|
<< " --fps displays the number of frames per second the\n"
|
|
<< " game is currently running at, in a corner of\n"
|
|
<< " the screen.\n"
|
|
<< " -f, --fullscreen runs the game in full screen mode.\n"
|
|
<< " --gunzip <infile>.gz decompresses a file (<infile>.gz) in gzip format\n"
|
|
<< " and stores it without the .gz suffix.\n"
|
|
<< " <infile>.gz will be removed.\n"
|
|
<< " --gzip <infile> compresses a file (<infile>) in gzip format,\n"
|
|
<< " stores it as <infile>.gz and removes <infile>.\n"
|
|
<< " -h, --help prints this message and exits.\n"
|
|
<< " -l, --load <file> loads the save <file> from the standard save\n"
|
|
<< " game directory.\n"
|
|
#ifndef DISABLE_EDITOR
|
|
<< " When launching the map editor via -e, the map\n"
|
|
<< " <file> is loaded, relative to the current\n"
|
|
<< " directory. If it is a directory, the editor\n"
|
|
<< " will start with a load map dialog opened there.\n"
|
|
#endif
|
|
<< " --log-<level>=<domain1>,<domain2>,...\n"
|
|
<< " sets the severity level of the log domains.\n"
|
|
<< " 'all' can be used to match any log domain.\n"
|
|
<< " Available levels: error, warning, info, debug.\n"
|
|
<< " By default the 'error' level is used.\n"
|
|
<< " --logdomains [filter] lists defined log domains containing filter and\n"
|
|
<< " exits.\n"
|
|
<< " --max-fps the maximum fps the game tries to run at. Values\n"
|
|
<< " should be between 1 and 1000, the default is 50.\n"
|
|
<< " -m, --multiplayer starts a multiplayer game. There are additional\n"
|
|
<< " options that can be used as explained below:\n"
|
|
<< " --ai_config<number>=value selects a configuration file to load for this side.\n"
|
|
<< " --algorithm<number>=value selects a non-standard algorithm to be used by\n"
|
|
<< " the AI controller for this side.\n"
|
|
<< " --controller<number>=value selects the controller for this side.\n"
|
|
<< " --era=value selects the era to be played in by its id.\n"
|
|
<< " --exit-at-end exit Wesnoth at the end of the scenario.\n"
|
|
<< " --nogui runs the game without the GUI. Must appear before\n"
|
|
<< " --multiplayer to have the desired effect.\n"
|
|
<< " --parm<number>=name:value sets additional parameters for this side.\n"
|
|
<< " --scenario=value selects a multiplayer scenario. The default\n"
|
|
<< " scenario is \"multiplayer_The_Freelands\".\n"
|
|
<< " --side<number>=value selects a faction of the current era for this\n"
|
|
<< " side by id.\n"
|
|
<< " --turns=value sets the number of turns. The default is \"50\".\n"
|
|
<< " --no-delay runs the game without any delays.\n"
|
|
<< " --nocache disables caching of game data.\n"
|
|
<< " --nomusic runs the game without music.\n"
|
|
<< " --nosound runs the game without sounds and music.\n"
|
|
<< " --path prints the path to the data directory and exits.\n"
|
|
<< " --preprocess, -p[=<define1>,<define2>,...] <file/folder> <target directory>\n"
|
|
<< " preprocesses a specified file/folder. The preprocessed\n"
|
|
<< " file(s) will be written in the specified target\n"
|
|
<< " directory: a plain cfg file and a processed cfg file.\n"
|
|
<< " define1,define2,... - the extra defines will\n"
|
|
<< " be added before processing the files. If you add\n"
|
|
<< " them you must add the '=' character before.\n"
|
|
<< " If 'SKIP_CORE' is in the define list the\n"
|
|
<< " data/core won't be preprocessed.\n"
|
|
<< " --preprocess-input-macros <source file>\n"
|
|
<< " used only by the '--preprocess' command.\n"
|
|
<< " Specifies a file that contains [preproc_define]s\n"
|
|
<< " to be included before preprocessing.\n"
|
|
<< " --preprocess-output-macros [<target file>]\n"
|
|
<< " used only by the '--preprocess' command.\n"
|
|
<< " Will output all preprocessed macros in the target file.\n"
|
|
<< " If the file is not specified the output will be\n"
|
|
<< " file '_MACROS_.cfg' in the target directory of\n"
|
|
<< " preprocess's command. This switch should be typed\n"
|
|
<< " before the --preprocess command.\n"
|
|
<< " -r, --resolution XxY sets the screen resolution. Example: -r 800x600\n"
|
|
<< " --rng-seed <number> seeds the random number generator with number\n"
|
|
<< " Example: --rng-seed 0\n"
|
|
<< " --smallgui allows to use screen resolutions down to 800x480\n"
|
|
<< " and resizes a few interface elements.\n"
|
|
<< " --screenshot <map> <output> Saves a screenshot of <map> to <output> without\n"
|
|
<< " initializing a screen. Editor must be compiled\n"
|
|
<< " in for this to work.\n"
|
|
<< " -s, --server [<host>] connects to the host if specified\n"
|
|
<< " or to the first host in your preferences.\n"
|
|
<< " -t, --test runs the game in a small test scenario.\n"
|
|
<< " --validcache assumes that the cache is valid. (dangerous)\n"
|
|
<< " -v, --version prints the game's version number and exits.\n"
|
|
<< " -w, --windowed runs the game in windowed mode.\n"
|
|
<< " --with-replay replays the file loaded with the --load option.\n"
|
|
<< " --new-widgets there is a new WIP widget toolkit this switch\n"
|
|
<< " enables the new toolkit (VERY EXPERIMENTAL don't\n"
|
|
<< " file bug reports since most are known).\n"
|
|
<< " Parts of the library are deemed stable and will\n"
|
|
<< " work without this switch.\n"
|
|
;
|
|
return 0;
|
|
} else if(val == "--version" || val == "-v") {
|
|
std::cout << "Battle for Wesnoth" << " " << game_config::version
|
|
<< "\n";
|
|
return 0;
|
|
} else if (val == "--config-path") {
|
|
std::cout << get_user_data_dir() << '\n';
|
|
return 0;
|
|
} else if(val == "--path") {
|
|
std::cout << game_config::path
|
|
<< "\n";
|
|
return 0;
|
|
}
|
|
#ifndef DISABLE_EDITOR
|
|
else if (val == "--screenshot" ) {
|
|
if(!(argc > arg + 2)) {
|
|
std::cerr << "format of " << val << " command: " << val << " <map file> <output file>\n";
|
|
return 2;
|
|
}
|
|
static char opt[] = "SDL_VIDEODRIVER=dummy";
|
|
SDL_putenv(opt);
|
|
}
|
|
#endif
|
|
else if(val == "--config-dir") {
|
|
if (argc <= ++arg)
|
|
break;
|
|
set_preferences_dir(argv[arg]);
|
|
} else if(val == "--data-dir") {
|
|
if(arg +1 != argc) {
|
|
++arg;
|
|
const std::string datadir(argv[arg]);
|
|
std::cerr << "Overriding data directory with " << datadir << std::endl;
|
|
#ifdef _WIN32
|
|
// use c_str to ensure that index 1 points to valid element since c_str() returns null-terminated string
|
|
if(datadir.c_str()[1] == ':') {
|
|
#else
|
|
if(datadir[0] == '/') {
|
|
#endif
|
|
game_config::path = datadir;
|
|
} else {
|
|
game_config::path = get_cwd() + '/' + datadir;
|
|
}
|
|
|
|
if(!is_directory(game_config::path)) {
|
|
std::cerr << "Could not find directory '" << game_config::path << "'\n";
|
|
throw config::error("directory not found");
|
|
}
|
|
|
|
// don't update font as we already updating it in game ctor
|
|
}
|
|
else
|
|
std::cerr << "please specify a data directory\n";
|
|
} else if (val.substr(0, 6) == "--log-") {
|
|
size_t p = val.find('=');
|
|
if (p == std::string::npos) {
|
|
std::cerr << "unknown option: " << val << '\n';
|
|
return 2;
|
|
}
|
|
std::string s = val.substr(6, p - 6);
|
|
int severity;
|
|
if (s == "error") severity = 0;
|
|
else if (s == "warning") severity = 1;
|
|
else if (s == "info") severity = 2;
|
|
else if (s == "debug") severity = 3;
|
|
else {
|
|
std::cerr << "unknown debug level: " << s << '\n';
|
|
return 2;
|
|
}
|
|
while (p != std::string::npos) {
|
|
size_t q = val.find(',', p + 1);
|
|
s = val.substr(p + 1, q == std::string::npos ? q : q - (p + 1));
|
|
if (!lg::set_log_domain_severity(s, severity)) {
|
|
std::cerr << "unknown debug domain: " << s << '\n';
|
|
return 2;
|
|
}
|
|
p = q;
|
|
}
|
|
} else if(val == "--gzip") {
|
|
if(argc != arg + 2) {
|
|
std::cerr << "format of " << val << " command: " << val << " <input file>\n";
|
|
return 2;
|
|
}
|
|
|
|
const std::string input_file(argv[arg + 1]);
|
|
const std::string output_file(input_file + ".gz");
|
|
gzip_encode(input_file, output_file);
|
|
|
|
} else if(val == "--gunzip") {
|
|
if(argc != arg + 2) {
|
|
std::cerr << "format of " << val << " command: " << val << " <input file>\n";
|
|
return 2;
|
|
}
|
|
|
|
const std::string input_file(argv[arg + 1]);
|
|
if(! is_gzip_file(input_file)) {
|
|
|
|
std::cerr << "file '" << input_file << "'isn't a .gz file\n";
|
|
return 2;
|
|
}
|
|
const std::string output_file(
|
|
input_file, 0, input_file.length() - 3);
|
|
|
|
gzip_decode(input_file, output_file);
|
|
|
|
} else if(val == "--logdomains") {
|
|
std::string filter;
|
|
if(arg + 1 != argc) {
|
|
++arg;
|
|
filter = argv[arg];
|
|
}
|
|
std::cout << lg::list_logdomains(filter);
|
|
return 0;
|
|
} else if(val == "--rng-seed") {
|
|
if (argc <= ++arg) {
|
|
std::cerr << "format of \" " << val << " " << argv[arg] << " \" is bad\n";
|
|
return 2;
|
|
}
|
|
srand(lexical_cast_default<unsigned int>(argv[arg]));
|
|
} else if (val == "--preprocess-input-macros") {
|
|
if (arg + 1 < argc)
|
|
{
|
|
++arg;
|
|
std::string file = argv[arg];
|
|
if (file_exists(file) == false)
|
|
{
|
|
std::cerr << "please specify an existing file. File "<< file <<" doesn't exist.\n";
|
|
return 1;
|
|
}
|
|
|
|
std::cerr << SDL_GetTicks() << " Reading cached defines from: " << file << "\n";
|
|
|
|
config cfg;
|
|
std::string error_log;
|
|
scoped_istream stream = istream_file(file);
|
|
read(cfg, *stream);
|
|
|
|
int read = 0;
|
|
// use static preproc_define::read_pair(config) to make a object
|
|
foreach (const config::any_child &value, cfg.all_children_range()) {
|
|
const preproc_map::value_type def = preproc_define::read_pair(value.cfg);
|
|
preproc.input_macros_[def.first] = def.second;
|
|
++read;
|
|
}
|
|
std::cerr << SDL_GetTicks() << " Read " << read << " defines.\n";
|
|
}
|
|
else {
|
|
std::cerr << "please specify input macros file.\n";
|
|
return 2;
|
|
}
|
|
} else if (val == "--preprocess-output-macros") {
|
|
preproc.output_macros_path_ = "true";
|
|
if (arg + 1 < argc && argv[arg+1][0] != '-')
|
|
{
|
|
++arg;
|
|
preproc.output_macros_path_ = argv[arg];
|
|
}
|
|
} else if (val.find("--preprocess") == 0 || val.find("-p") == 0){
|
|
if (arg + 2 < argc){
|
|
++arg;
|
|
const std::string resourceToProcess(argv[arg]);
|
|
++arg;
|
|
const std::string targetDir(argv[arg]);
|
|
|
|
Uint32 startTime = SDL_GetTicks();
|
|
// if the users add the SKIP_CORE define we won't preprocess data/core
|
|
bool skipCore = false;
|
|
// the 'core_defines_map' is the one got from data/core macros
|
|
preproc_map defines_map(preproc.input_macros_);
|
|
std::string error_log;
|
|
|
|
// add the specified defines
|
|
size_t pos=std::string::npos;
|
|
if (val.find("--preprocess=") == 0)
|
|
pos = val.find("=");
|
|
else if (val.find("-p=") == 0)
|
|
pos = val.find("=");
|
|
|
|
// we have some defines specified
|
|
if (pos != std::string::npos)
|
|
{
|
|
std::string tmp_val = val.substr(pos+1);
|
|
while (pos != std::string::npos)
|
|
{
|
|
size_t tmpPos = val.find(',', pos+1);
|
|
tmp_val = val.substr(pos + 1,
|
|
tmpPos == std::string::npos ? tmpPos : tmpPos - (pos+1));
|
|
pos = tmpPos;
|
|
|
|
if (tmp_val.empty()){
|
|
std::cerr << "empty define supplied\n";
|
|
continue;
|
|
}
|
|
|
|
LOG_PREPROC<<"adding define: "<< tmp_val<<'\n';
|
|
defines_map.insert(std::make_pair(tmp_val,
|
|
preproc_define(tmp_val)));
|
|
if (tmp_val == "SKIP_CORE")
|
|
{
|
|
std::cerr << "'SKIP_CORE' defined.\n";
|
|
skipCore = true;
|
|
}
|
|
}
|
|
std::cerr << "added " << defines_map.size() << " defines.\n";
|
|
}
|
|
|
|
// preprocess core macros first if we don't skip the core
|
|
if (skipCore == false)
|
|
{
|
|
std::cerr << "preprocessing common macros from 'data/core' ...\n";
|
|
preprocess_resource(game_config::path + "/data/core",&defines_map);
|
|
std::cerr << "acquired " << (defines_map.size() - preproc.input_macros_.size())
|
|
<< " 'data/core' defines.\n";
|
|
}
|
|
else
|
|
std::cerr << "skipped 'data/core'\n";
|
|
|
|
// preprocess resource
|
|
std::cerr << "preprocessing specified resource: "
|
|
<< resourceToProcess << " ...\n";
|
|
preprocess_resource(resourceToProcess, &defines_map, true,true, targetDir);
|
|
std::cerr << "acquired " << (defines_map.size() - preproc.input_macros_.size())
|
|
<< " total defines.\n";
|
|
|
|
if (preproc.output_macros_path_ != "false")
|
|
{
|
|
std::string outputPath = targetDir + "/_MACROS_.cfg";
|
|
if (preproc.output_macros_path_ != "true")
|
|
outputPath = preproc.output_macros_path_;
|
|
|
|
std::cerr << "writing '" << outputPath << "' with "
|
|
<< defines_map.size() << " defines.\n";
|
|
|
|
scoped_ostream out = ostream_file(outputPath);
|
|
if (!out->fail())
|
|
{
|
|
config_writer writer(*out,false);
|
|
|
|
for(preproc_map::iterator itor = defines_map.begin();
|
|
itor != defines_map.end(); ++itor)
|
|
{
|
|
(*itor).second.write(writer, (*itor).first);
|
|
}
|
|
}
|
|
else
|
|
std::cerr << "couldn't open the file.\n";
|
|
}
|
|
|
|
std::cerr << "preprocessing finished. Took "<< SDL_GetTicks() - startTime << " ticks.\n";
|
|
return 0;
|
|
}
|
|
else{
|
|
std::cerr << "Please specify a source file/folder and a target folder\n";
|
|
return 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Not the most intuitive solution, but I wanted to leave current semantics for now
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* I would prefer to setup locale first so that early error
|
|
* messages can get localized, but we need the game_controller
|
|
* initialized to have get_intl_dir() to work. Note: setlocale()
|
|
* does not take GUI language setting into account.
|
|
*/
|
|
static void init_locale() {
|
|
#ifdef _WIN32
|
|
std::setlocale(LC_ALL, "English");
|
|
#else
|
|
std::setlocale(LC_ALL, "C");
|
|
std::setlocale(LC_MESSAGES, "");
|
|
#endif
|
|
const std::string& intl_dir = get_intl_dir();
|
|
bindtextdomain (PACKAGE, intl_dir.c_str());
|
|
bind_textdomain_codeset (PACKAGE, "UTF-8");
|
|
bindtextdomain (PACKAGE "-lib", intl_dir.c_str());
|
|
bind_textdomain_codeset (PACKAGE "-lib", "UTF-8");
|
|
textdomain (PACKAGE);
|
|
}
|
|
|
|
/**
|
|
* Setups the game environment and enters
|
|
* the titlescreen or game loops.
|
|
*/
|
|
static int do_gameloop(int argc, char** argv)
|
|
{
|
|
srand(time(NULL));
|
|
|
|
int finished = process_command_args(argc, argv);
|
|
if(finished != -1) {
|
|
return finished;
|
|
}
|
|
|
|
//ensure recorder has an actually random seed instead of what it got during
|
|
//static initialization (befire any srand() call)
|
|
recorder.set_seed(rand());
|
|
|
|
game_controller game(argc,argv);
|
|
const int start_ticks = SDL_GetTicks();
|
|
|
|
init_locale();
|
|
|
|
bool res;
|
|
|
|
// do initialize fonts before reading the game config, to have game
|
|
// config error messages displayed. fonts will be re-initialized later
|
|
// when the language is read from the game config.
|
|
res = font::load_font_config();
|
|
if(res == false) {
|
|
std::cerr << "could not initialize fonts\n";
|
|
return 1;
|
|
}
|
|
|
|
res = game.init_language();
|
|
if(res == false) {
|
|
std::cerr << "could not initialize the language\n";
|
|
return 1;
|
|
}
|
|
|
|
res = game.init_video();
|
|
if(res == false) {
|
|
std::cerr << "could not initialize display\n";
|
|
return 1;
|
|
}
|
|
|
|
const cursor::manager cursor_manager;
|
|
cursor::set(cursor::WAIT);
|
|
|
|
gui2::init();
|
|
const gui2::event::tmanager gui_event_manager;
|
|
|
|
loadscreen::global_loadscreen_manager loadscreen_manager(game.disp().video());
|
|
|
|
loadscreen::global_loadscreen->increment_progress(5, _("Loading game configuration."));
|
|
res = game.init_config();
|
|
if(res == false) {
|
|
std::cerr << "could not initialize game config\n";
|
|
return 1;
|
|
}
|
|
loadscreen::global_loadscreen->increment_progress(10, _("Re-initialize fonts for the current language."));
|
|
|
|
res = font::load_font_config();
|
|
if(res == false) {
|
|
std::cerr << "could not re-initialize fonts for the current language\n";
|
|
return 1;
|
|
}
|
|
|
|
loadscreen::global_loadscreen->increment_progress(0, _("Searching for installed add-ons."));
|
|
refresh_addon_version_info_cache();
|
|
|
|
#if defined(_X11) && !defined(__APPLE__)
|
|
SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
|
|
#endif
|
|
|
|
config tips_of_day;
|
|
|
|
loadscreen::global_loadscreen->set_progress(100, _("Loading title screen."));
|
|
|
|
LOG_CONFIG << "time elapsed: "<< (SDL_GetTicks() - start_ticks) << " ms\n";
|
|
|
|
for (;;)
|
|
{
|
|
game::exception::sticky = NULL;
|
|
|
|
// reset the TC, since a game can modify it, and it may be used
|
|
// by images in add-ons or campaigns dialogs
|
|
image::set_team_colors();
|
|
|
|
statistics::fresh_stats();
|
|
|
|
if (!game.is_loading()) {
|
|
const config &cfg = game.game_config().child("titlescreen_music");
|
|
if (cfg) {
|
|
sound::play_music_repeatedly(game_config::title_music);
|
|
foreach (const config &i, cfg.child_range("music")) {
|
|
sound::play_music_config(i);
|
|
}
|
|
sound::commit_music_changes();
|
|
} else {
|
|
sound::empty_playlist();
|
|
sound::stop_music();
|
|
}
|
|
}
|
|
|
|
if(game.play_test() == false) {
|
|
return 0;
|
|
}
|
|
|
|
if(game.play_multiplayer_mode() == false) {
|
|
return 0;
|
|
}
|
|
|
|
if(game.play_screenshot_mode() == false) {
|
|
return 0;
|
|
}
|
|
|
|
recorder.clear();
|
|
|
|
//Start directly a campaign
|
|
if(game.goto_campaign() == false){
|
|
if (game.jump_to_campaign_id().empty())
|
|
continue; //Go to main menu
|
|
else
|
|
return 1; //we got an error starting the campaign from command line
|
|
}
|
|
|
|
//Start directly a multiplayer
|
|
//Eventually with a specified server
|
|
if(game.goto_multiplayer() == false){
|
|
continue; //Go to main menu
|
|
}
|
|
#ifndef DISABLE_EDITOR
|
|
if (game.goto_editor() == false) {
|
|
return 0;
|
|
}
|
|
#endif
|
|
loadscreen_manager.reset();
|
|
|
|
gui::TITLE_RESULT res = game.is_loading() ? gui::LOAD_GAME : gui::NOTHING;
|
|
|
|
if(gui2::new_widgets) {
|
|
const preferences::display_manager disp_manager(&game.disp());
|
|
const hotkey::basic_handler key_handler(&game.disp());
|
|
|
|
const font::floating_label_context label_manager;
|
|
|
|
cursor::set(cursor::NORMAL);
|
|
gui2::ttitle_screen dlg;
|
|
dlg.show(game.disp().video());
|
|
|
|
res = static_cast<gui::TITLE_RESULT>(dlg.get_retval());
|
|
|
|
} else {
|
|
while(res == gui::NOTHING) {
|
|
res = gui::show_title(game.disp(),tips_of_day);
|
|
if (res == gui::REDRAW_BACKGROUND) {
|
|
gui::set_background_dirty();
|
|
res = gui::NOTHING;
|
|
}
|
|
}
|
|
}
|
|
|
|
game_controller::RELOAD_GAME_DATA should_reload = game_controller::RELOAD_DATA;
|
|
|
|
if(res == gui::QUIT_GAME) {
|
|
LOG_GENERAL << "quitting game...\n";
|
|
return 0;
|
|
} else if(res == gui::LOAD_GAME) {
|
|
if(game.load_game() == false) {
|
|
game.clear_loaded_game();
|
|
res = gui::NOTHING;
|
|
continue;
|
|
}
|
|
|
|
should_reload = game_controller::NO_RELOAD_DATA;
|
|
} else if(res == gui::TUTORIAL) {
|
|
game.set_tutorial();
|
|
} else if(res == gui::NEW_CAMPAIGN) {
|
|
if(game.new_campaign() == false) {
|
|
continue;
|
|
}
|
|
} else if(res == gui::MULTIPLAYER) {
|
|
if (!game_config::mp_debug) {
|
|
game_config::debug = false;
|
|
}
|
|
if(game.play_multiplayer() == false) {
|
|
continue;
|
|
}
|
|
} else if(res == gui::CHANGE_LANGUAGE) {
|
|
if (game.change_language()) {
|
|
tips_of_day.clear();
|
|
t_string::reset_translations();
|
|
image::flush_cache();
|
|
gui::set_background_dirty();
|
|
}
|
|
continue;
|
|
} else if(res == gui::EDIT_PREFERENCES) {
|
|
game.show_preferences();
|
|
if (game.disp().video().modeChanged()) {
|
|
gui::set_background_dirty();
|
|
}
|
|
continue;
|
|
} else if(res == gui::SHOW_ABOUT) {
|
|
about::show_about(game.disp());
|
|
continue;
|
|
} else if(res == gui::SHOW_HELP) {
|
|
help::help_manager help_manager(&game.game_config(), NULL);
|
|
help::show_help(game.disp());
|
|
continue;
|
|
} else if(res == gui::GET_ADDONS) {
|
|
try {
|
|
manage_addons(game.disp());
|
|
} catch(config_changed_exception const&) {
|
|
game.reload_changed_game_config();
|
|
}
|
|
continue;
|
|
} else if(res == gui::BEG_FOR_UPLOAD) {
|
|
game.show_upload_begging();
|
|
continue;
|
|
} else if(res == gui::RELOAD_GAME_DATA) {
|
|
loadscreen::global_loadscreen_manager loadscreen(game.disp().video());
|
|
game.reload_changed_game_config();
|
|
image::flush_cache();
|
|
continue;
|
|
#ifndef DISABLE_EDITOR
|
|
} else if(res == gui::START_MAP_EDITOR) {
|
|
///@todo editor can ask the game to quit completely
|
|
if (game.start_editor() == editor::EXIT_QUIT_TO_DESKTOP) {
|
|
return 0;
|
|
} else {
|
|
gui::set_background_dirty();
|
|
}
|
|
continue;
|
|
#endif
|
|
}
|
|
|
|
if (recorder.at_end()){
|
|
game.launch_game(should_reload);
|
|
}
|
|
else{
|
|
game.play_replay();
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifndef DISABLE_POOL_ALLOC
|
|
extern "C" {
|
|
void init_custom_malloc();
|
|
}
|
|
#endif
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
#ifndef DISABLE_POOL_ALLOC
|
|
init_custom_malloc();
|
|
#endif
|
|
if(SDL_Init(SDL_INIT_TIMER) < 0) {
|
|
fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
|
|
return(1);
|
|
}
|
|
|
|
try {
|
|
std::cerr << "Battle for Wesnoth v" << game_config::revision << '\n';
|
|
const time_t t = time(NULL);
|
|
std::cerr << "Started on " << ctime(&t) << "\n";
|
|
|
|
const std::string exe_dir = get_exe_dir();
|
|
if(!exe_dir.empty() && file_exists(exe_dir + "/data/_main.cfg")) {
|
|
std::cerr << "Automatically found a possible data directory at "
|
|
<< exe_dir << '\n';
|
|
game_config::path = exe_dir;
|
|
}
|
|
|
|
const int res = do_gameloop(argc,argv);
|
|
safe_exit(res);
|
|
} catch(CVideo::error&) {
|
|
std::cerr << "Could not initialize video. Exiting.\n";
|
|
return 1;
|
|
} catch(font::manager::error&) {
|
|
std::cerr << "Could not initialize fonts. Exiting.\n";
|
|
return 1;
|
|
} catch(config::error& e) {
|
|
std::cerr << e.message << "\n";
|
|
return 1;
|
|
} catch(gui::button::error&) {
|
|
std::cerr << "Could not create button: Image could not be found\n";
|
|
} catch(CVideo::quit&) {
|
|
//just means the game should quit
|
|
} catch(end_level_exception&) {
|
|
std::cerr << "caught end_level_exception (quitting)\n";
|
|
} catch(twml_exception& e) {
|
|
std::cerr << "WML exception:\nUser message: "
|
|
<< e.message << "\nDev message: " << e.dev_message << '\n';
|
|
return 1;
|
|
} catch(game_logic::formula_error& e) {
|
|
std::cerr << e.what()
|
|
<< "\n\nGame will be aborted.\n";
|
|
} catch(game::error &) {
|
|
// A message has already been displayed.
|
|
return 1;
|
|
} catch(std::bad_alloc&) {
|
|
std::cerr << "Ran out of memory. Aborted.\n";
|
|
return ENOMEM;
|
|
}
|
|
|
|
return 0;
|
|
} // end main
|
|
|