On Tue, 2005-12-20 at 10:39 +0100, [EMAIL PROTECTED] wrote:
> ok, apprently, we are "IRC incompatible" so we'll have to discuss it on
> the ML :)
Hmm, OK.
> > I'll look at it again though since you feel strongly about it.
> >
> yes, thx
OK, done (plus the other things you requested, but left the nag screen).
While the ordering isn't perfect (elements seem to get added backwards,
but children forwards), it *is* neater. See below. I also fixed a bug
where I'd only list unrecalled high-level units when loading a
mid-scenario save.
> > It shouldn't do anything in those cases. Will test.
> >
> ok, good... we might want to log things about MP games, but best leave
> that out for the moment
Yep. It is a little easier now I moved to an object: I just hand a
non-logging object through those paths (eg. multiplayer) I don't want to
log.
> my original idea was to have it be it's own class, but if you find a class
> in which it fits, even better...
Yeah, it didn't really fit as a member of anything else. The behaviour
has changed a little in that each new load/start creates a new file, but
the filename numbers order them so I think that's OK.
Here's an example (chopped for length a little; I played for a while
just to be thorough 8). The patch follows. Comments welcome!
[start]
campaign="CAMPAIGN_HEIR_TO_THE_THRONE"
difficulty="NORMAL"
gold="173"
scenario="Gryphon_Mountain"
time="1408"
turn="14"
version="1.1-svn"
[special-unit]
experience="11"
level="3"
name="Konrad"
[/special-unit]
[unit-count]
...
[Elder Mage]
count="1"
[/Elder Mage]
[Elvish Avenger]
count="1"
[/Elvish Avenger]
...
[/unit-count]
[/start]
[victory]
scenario="Gryphon_Mountain"
time="1493"
turns="15"
[/victory]
[start]
campaign="CAMPAIGN_HEIR_TO_THE_THRONE"
difficulty="NORMAL"
gold="328"
scenario="The_Ford_of_Abez"
time="1504"
version="1.1-svn"
[special-unit]
experience="11"
level="3"
name="Konrad"
[/special-unit]
[unit-count]
...
[Elder Mage]
count="1"
[/Elder Mage]
[Elvish Avenger]
count="2"
[/Elvish Avenger]
...
[/unit-count]
[/start]
Index: upload_log.cpp
===================================================================
--- upload_log.cpp (revision 0)
+++ upload_log.cpp (revision 0)
@@ -0,0 +1,167 @@
+/* $Id$ */
+/*
+ Copyright (C) 2005 by Rusty Russell <[EMAIL PROTECTED]>
+ 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"
+
+#define GETTEXT_DOMAIN "wesnoth"
+
+#include "gettext.hpp"
+#include "filesystem.hpp"
+#include "preferences.hpp"
+#include "serialization/parser.hpp"
+#include "show_dialog.hpp"
+#include "upload_log.hpp"
+
+#include <vector>
+#include <string>
+#include <iomanip>
+#include <sstream>
+
+// Helper to get a new filename in the upload dir.
+static std::string next_filename()
+{
+ std::vector<std::string> files;
+ std::stringstream fname;
+ unsigned int num = 1;
+
+ // These are sorted, so we can simply add one to last one.
+ get_files_in_dir(get_upload_dir(), &files);
+
+ if (!files.empty())
+ num = lexical_cast<int>(files.back())+1;
+
+ fname << std::setw(8) << std::setfill('0') << num;
+ return get_upload_dir() + "/" + fname.str();
+}
+
+upload_log::upload_log(bool enable)
+{
+ if (enable)
+ out_ = ostream_file(next_filename());
+}
+
+upload_log::~upload_log()
+{
+ if (out_) {
+ // We check preferences at last minute, in case they changed.
+ if (preferences::upload_log()) {
+ write(*out_, config_);
+ }
+ delete out_;
+ }
+}
+
+config &upload_log::add_entry(const std::string &str)
+{
+ config &child = config_.add_child(str);
+ child["time"] = lexical_cast<std::string>(SDL_GetTicks() / 1000);
+ return child;
+}
+
+// User finishes a scenario.
+void upload_log::defeat(game_state &state)
+{
+ config &e = add_entry("defeat");
+ e["scenario"] = state.scenario;
+}
+
+void upload_log::victory(game_state &state, int turns)
+{
+ config &e = add_entry("victory");
+ e["scenario"] = state.scenario;
+ e["turns"] = lexical_cast<std::string>(turns);
+}
+
+void upload_log::add_unit(config &entry,
+ const unit &unit,
std::map<std::string, int> *tally)
+{
+ // Record details for significant units
+ if (unit.can_recruit()) {
+ config &uc = entry.add_child("special-unit");
+ uc["name"] = unit.name();
+ uc["level"] = lexical_cast<std::string>(unit.type().level());
+ uc["experience"] = lexical_cast<std::string>(unit.experience());
+ } else if (unit.type().level() > 1) {
+ (*tally)[unit.type().id()]++;
+ }
+}
+
+// User starts a game (may be new campaign or saved).
+void upload_log::start(game_state &state, const team &team,
+ int team_number,
+ const unit_map &units, const
t_string &turn)
+{
+ config &e = add_entry("start");
+ std::map<std::string, int> tally;
+
+ e["campaign"] = state.campaign_define;
+ e["difficulty"] = state.difficulty;
+ e["scenario"] = state.scenario;
+ if (!state.version.empty())
+ e["version"] = state.version;
+ if (!turn.empty())
+ e["turn"] = turn;
+ e["gold"] = lexical_cast<std::string>(team.gold());
+
+ // We seem to have to walk the map to find some units, and the player's
+ // available_units for the rest.
+ for (unit_map::const_iterator un = units.begin(); un != units.end();
++un){
+ if (un->second.side() == team_number) {
+ add_unit(e, un->second, &tally);
+ }
+ }
+ // FIXME: Assumes first player is "us"; is that valid?
+ player_info &player = state.players.begin()->second;
+ for (std::vector<unit>::iterator i = player.available_units.begin();
+ i != player.available_units.end();
+ ++i) {
+ add_unit(e, *i, &tally);
+ }
+
+ if (!tally.empty()) {
+ config &t = e.add_child("unit-count");
+
+ for (std::map<std::string, int>::iterator i = tally.begin();
+ i != tally.end();
+ i++) {
+ config &unit = t.add_child(i->first);
+ unit["count"] = lexical_cast<std::string>(i->second);
+ }
+ }
+}
+
+// Campaign ended successfully.
+void upload_log::end(game_state &state)
+{
+ config &e = add_entry("end");
+ e["campaign"] = state.campaign_define;
+}
+
+void upload_log_dialog::show_beg_dialog(display& disp)
+{
+ int res;
+ std::vector<std::string> experience_options;
+
+ experience_options.push_back(_("I am not used to strategy games"));
+ experience_options.push_back(_("I have played similar strategy games"));
+ experience_options.push_back(_("I have mastered similar strategy
games"));
+ experience_options.push_back(_("I have played Wesnoth before"));
+ experience_options.push_back(_("I am an experienced Wesnoth player"));
+
+ res = gui::show_dialog(disp, NULL,
+ _("Help us make Wesnoth better for you!"),
+ _("Wesnoth relies on volunteers like yourself.
We particularly need feedback from beginners and new players, so we can ensure
Wesnoth is as much fun as possible. Please allow Wesnoth to upload an
anonymous log about your gameplay to wesnoth.org for this purpose.\nSelect your
approximate experience level or select cancel to disable this feature:"),
+ gui::OK_CANCEL, &experience_options);
+ preferences::set_upload_log(res != -1);
+ if (res != -1)
+ preferences::set_upload_experience_level(res);
+}
Index: playlevel.cpp
===================================================================
--- playlevel.cpp (revision 9227)
+++ playlevel.cpp (working copy)
@@ -42,6 +42,7 @@
#include "statistics.hpp"
#include "tooltips.hpp"
#include "unit_display.hpp"
+#include "upload_log.hpp"
#include "util.hpp"
#include "video.hpp"
@@ -118,7 +119,8 @@
LEVEL_RESULT play_level(const game_data& gameinfo, const config& game_config,
config const* level, CVideo& video,
game_state& state_of_game,
- const std::vector<config*>& story)
+ const std::vector<config*>& story,
+ upload_log &log)
{
//if the recorder has no event, adds an "game start" event to the
//recorder, whose only goal is to initialize the RNG
@@ -316,6 +318,8 @@
LOG_NG << "scrolling... " << (SDL_GetTicks() - ticks) << "\n";
if(first_human_team != -1) {
+ log.start(state_of_game, teams[first_human_team],
first_human_team+1,
+ units, loading_game ?
state_of_game.get_variable("turn_number") : "");
LOG_NG << "b " << (SDL_GetTicks() - ticks) << "\n";
gui.scroll_to_tile(map.starting_position(first_human_team + 1).x,
map.starting_position(first_human_team + 1).y, display::WARP);
@@ -605,14 +609,15 @@
if(end_level.result == QUIT) {
return end_level.result;
} else if(end_level.result == DEFEAT) {
+ log.defeat(state_of_game);
try {
game_events::fire("defeat");
} catch(end_level_exception&) {
}
- if (!obs)
+ if (!obs) {
return DEFEAT;
- else
+ } else
return QUIT;
} else if (end_level.result == VICTORY || end_level.result ==
LEVEL_CONTINUE ||
end_level.result == LEVEL_CONTINUE_NO_SAVE) {
@@ -621,6 +626,10 @@
} catch(end_level_exception&) {
}
+ if (end_level.result == VICTORY) {
+ log.victory(state_of_game, status.turn());
+ }
+
if(state_of_game.scenario == (*level)["id"]) {
state_of_game.scenario =
(*level)["next_scenario"];
}
Index: filesystem.cpp
===================================================================
--- filesystem.cpp (revision 9227)
+++ filesystem.cpp (working copy)
@@ -283,6 +283,12 @@
return get_dir(dir_path);
}
+std::string get_upload_dir()
+{
+ const std::string dir_path = get_user_data_dir() + "/upload";
+ return get_dir(dir_path);
+}
+
std::string get_dir(const std::string& dir_path)
{
#ifdef _WIN32
Index: upload_log.hpp
===================================================================
--- upload_log.hpp (revision 0)
+++ upload_log.hpp (revision 0)
@@ -0,0 +1,53 @@
+/* $Id$ */
+/*
+ Copyright (C) 2005 by Rusty Russell <[EMAIL PROTECTED]>
+ 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 UPLOAD_LOG_H_INCLUDED
+#define UPLOAD_LOG_H_INCLUDED
+#include "config.hpp"
+#include "display.hpp"
+#include "gamestatus.hpp"
+#include "team.hpp"
+#include "tstring.hpp"
+#include "unit.hpp"
+
+struct upload_log
+{
+ upload_log(bool enable);
+ ~upload_log();
+
+ // User finishes a level.
+ void defeat(game_state &state);
+ void victory(game_state &state, int turn);
+
+ // User starts a game (may be new campaign or saved).
+ void start(game_state &state, const team &team,
+ int team_number, const unit_map &map, const t_string
&turn);
+
+ // Campaign ended successfully.
+ void end(game_state &state);
+
+private:
+ config &upload_log::add_entry(const std::string &str);
+ void add_unit(config &entry, const unit &unit, std::map<std::string,
int> *tally);
+
+ config config_;
+ std::ostream *out_;
+};
+
+namespace upload_log_dialog
+{
+ // Please please please upload stats?
+ void show_beg_dialog(display& disp);
+};
+
+#endif // UPLOAD_LOG_H_INCLUDED
Index: playlevel.hpp
===================================================================
--- playlevel.hpp (revision 9227)
+++ playlevel.hpp (working copy)
@@ -19,6 +19,7 @@
#include "game_config.hpp"
#include "unit_types.hpp"
+#include "upload_log.hpp"
#include <vector>
@@ -40,7 +41,8 @@
LEVEL_RESULT play_level(const game_data& gameinfo, const config&
terrain_config,
config const* level, CVideo& video,
game_state& state_of_game,
- const std::vector<config*>& story);
+ const std::vector<config*>& story,
+ upload_log &log);
namespace play{
void place_sides_in_preferred_locations(gamemap& map, const
config::child_list& sides);
Index: titlescreen.cpp
===================================================================
--- titlescreen.cpp (revision 9227)
+++ titlescreen.cpp (working copy)
@@ -28,6 +28,7 @@
#include "sdl_utils.hpp"
#include "show_dialog.hpp"
#include "titlescreen.hpp"
+#include "upload_log.hpp"
#include "util.hpp"
#include "video.hpp"
#include "serialization/parser.hpp"
@@ -289,6 +290,9 @@
update_whole_screen();
+ if (!preferences::upload_log_defined())
+ return BEG_FOR_UPLOAD;
+
LOG_DP << "entering interactive loop...\n";
for(;;) {
Index: Makefile.am
===================================================================
--- Makefile.am (revision 9227)
+++ Makefile.am (working copy)
@@ -102,6 +102,7 @@
unit.cpp \
unit_display.cpp \
unit_types.cpp \
+ upload_log.cpp \
variable.cpp \
video.cpp \
serialization/binary_or_text.cpp \
Index: playcampaign.cpp
===================================================================
--- playcampaign.cpp (revision 9227)
+++ playcampaign.cpp (working copy)
@@ -117,6 +117,7 @@
LEVEL_RESULT play_game(display& disp, game_state& state, const config&
game_config,
const game_data& units_data, CVideo& video,
+ upload_log &log,
io_type_t io_type)
{
std::string type = state.campaign_type;
@@ -197,7 +198,7 @@
if (state.label.empty())
state.label = (*scenario)["name"];
- LEVEL_RESULT res =
play_level(units_data,game_config,scenario,video,state,story);
+ LEVEL_RESULT res =
play_level(units_data,game_config,scenario,video,state,story,log);
state.snapshot = config();
if (res == DEFEAT) {
@@ -217,6 +218,7 @@
end.add_child("end_scenarios");
network::send_data(end);
}
+
//ask to save a replay of the game
if(res == VICTORY || res == DEFEAT) {
const std::string orig_scenario =
state.scenario;
@@ -380,6 +382,7 @@
return QUIT;
}
+ log.end(state);
return VICTORY;
}
Index: multiplayer.cpp
===================================================================
--- multiplayer.cpp (revision 9227)
+++ multiplayer.cpp (working copy)
@@ -236,6 +236,7 @@
mp::ui::result res;
game_state state;
network_game_manager m;
+ upload_log nolog(false);
gamelist.clear();
statistics::fresh_stats();
@@ -257,7 +258,7 @@
switch (res) {
case mp::ui::PLAY:
- play_game(disp, state, game_config, data, disp.video(),
IO_CLIENT);
+ play_game(disp, state, game_config, data, disp.video(), nolog,
IO_CLIENT);
recorder.clear();
break;
@@ -278,6 +279,7 @@
network::server_manager::TRY_CREATE_SERVER :
network::server_manager::NO_SERVER);
network_game_manager m;
+ upload_log nolog(false);
gamelist.clear();
statistics::fresh_stats();
@@ -298,7 +300,7 @@
switch (res) {
case mp::ui::PLAY:
- play_game(disp, state, game_config, data, disp.video(),
IO_SERVER);
+ play_game(disp, state, game_config, data, disp.video(), nolog,
IO_SERVER);
recorder.clear();
break;
Index: game.cpp
===================================================================
--- game.cpp (revision 9227)
+++ game.cpp (working copy)
@@ -51,6 +51,7 @@
#include "titlescreen.hpp"
#include "util.hpp"
#include "unit_types.hpp"
+#include "upload_log.hpp"
#include "unit.hpp"
#include "video.hpp"
#include "wassert.hpp"
@@ -106,6 +107,7 @@
bool change_language();
void show_preferences();
+ void show_upload_begging();
enum RELOAD_GAME_DATA { RELOAD_DATA, NO_RELOAD_DATA };
void play_game(RELOAD_GAME_DATA reload=RELOAD_DATA);
@@ -417,7 +419,8 @@
state_.scenario = "test";
try {
- ::play_game(disp(),state_,game_config_,units_data_,video_);
+ upload_log nolog(false);
+
::play_game(disp(),state_,game_config_,units_data_,video_,nolog);
} catch(game::load_game_exception& e) {
loaded_game_ = e.game;
loaded_game_show_replay_ = e.show_replay;
@@ -576,8 +579,9 @@
}
try {
+ upload_log nolog(false);
state_.snapshot = level;
- ::play_game(disp(),state_,game_config_,units_data_,video_);
+
::play_game(disp(),state_,game_config_,units_data_,video_,nolog);
//play_level(units_data_,game_config_,&level,video_,state_,story);
} catch(game::error& e) {
std::cerr << "caught error: '" << e.message << "'\n";
@@ -1242,6 +1246,13 @@
disp().redraw_everything();
}
+void game_controller::show_upload_begging()
+{
+ upload_log_dialog::show_beg_dialog(disp());
+
+ disp().redraw_everything();
+}
+
//this function reads the game configuration, searching for valid cached
copies first
void game_controller::read_game_cfg(const preproc_map& defines, config& cfg,
bool use_cache)
{
@@ -1454,7 +1465,10 @@
const binary_paths_manager bin_paths_manager(game_config_);
try {
- const LEVEL_RESULT result =
::play_game(disp(),state_,game_config_,units_data_,video_);
+ // Only record log for single-player games.
+ upload_log log(state_.campaign_type.empty() ||
state_.campaign_type == "scenario");
+
+ const LEVEL_RESULT result =
::play_game(disp(),state_,game_config_,units_data_,video_, log);
// don't show The End for multiplayer scenario
// change this if MP campaigns are implemented
if(result == VICTORY && (state_.campaign_type.empty() ||
state_.campaign_type != "multiplayer")) {
@@ -1731,6 +1745,9 @@
} else if(res == gui::SHOW_ABOUT) {
about::show_about(game.disp());
continue;
+ } else if(res == gui::BEG_FOR_UPLOAD) {
+ game.show_upload_begging();
+ continue;
}
if (recorder.at_end()){
Index: filesystem.hpp
===================================================================
--- filesystem.hpp (revision 9227)
+++ filesystem.hpp (working copy)
@@ -51,6 +51,7 @@
std::string get_cache_dir();
std::string get_intl_dir();
std::string get_screenshot_dir();
+std::string get_upload_dir();
std::string get_user_data_dir();
std::string get_cwd();
Index: preferences.cpp
===================================================================
--- preferences.cpp (revision 9227)
+++ preferences.cpp (working copy)
@@ -769,6 +769,38 @@
fps = value;
}
+bool upload_log()
+{
+ if (!upload_log_defined())
+ return false;
+ return prefs["upload_log"] == "yes";
+}
+
+bool upload_log_defined()
+{
+ const string_map::const_iterator i = prefs.values.find("upload_log");
+ return (i != prefs.values.end());
+}
+
+void set_upload_experience_level(int level)
+{
+ prefs["upload_experience"] = lexical_cast<std::string>(level);
+}
+
+void set_upload_log(bool value)
+{
+ prefs["upload_log"] = value ? "yes" : "no";
+
+ // We create a unique id for each person.
+ if (prefs.values.find("upload_id") == prefs.values.end()) {
+ // SDL_GetTicks() is a good value, as it depends on when they
clicked
+ srand(time(NULL));
+ prefs["upload_id"]
+ = lexical_cast<std::string>(rand())
+ + lexical_cast<std::string>(SDL_GetTicks());
+ }
+}
+
bool compress_saves()
{
return prefs["compress_saves"] != "no";
Index: titlescreen.hpp
===================================================================
--- titlescreen.hpp (revision 9227)
+++ titlescreen.hpp (working copy)
@@ -20,7 +20,7 @@
namespace gui {
enum TITLE_RESULT { TUTORIAL = 0, NEW_CAMPAIGN, MULTIPLAYER, LOAD_GAME,
- CHANGE_LANGUAGE, EDIT_PREFERENCES, SHOW_ABOUT, QUIT_GAME,
TITLE_CONTINUE };
+ CHANGE_LANGUAGE, EDIT_PREFERENCES, SHOW_ABOUT, QUIT_GAME,
TITLE_CONTINUE, BEG_FOR_UPLOAD };
TITLE_RESULT show_title(display& screen, config& tips_of_day, int* ntip);
Index: playcampaign.hpp
===================================================================
--- playcampaign.hpp (revision 9227)
+++ playcampaign.hpp (working copy)
@@ -16,6 +16,7 @@
#define PLAYCAMPAIGN_H_INCLUDED
#include "playlevel.hpp"
+#include "upload_log.hpp"
class display;
struct game_state;
@@ -31,6 +32,7 @@
LEVEL_RESULT play_game(display& disp, game_state& state, const config&
game_config,
const game_data& units_data, CVideo& video,
+ upload_log &log,
io_type_t io_type=IO_NONE);
Index: preferences_display.cpp
===================================================================
--- preferences_display.cpp (revision 9227)
+++ preferences_display.cpp (working copy)
@@ -21,6 +21,7 @@
#include "marked-up_text.hpp"
#include "preferences_display.hpp"
#include "show_dialog.hpp"
+#include "upload_log.hpp"
#include "video.hpp"
#include "wml_separators.hpp"
#include "widgets/button.hpp"
@@ -192,6 +193,7 @@
gui::slider music_slider_, sound_slider_, scroll_slider_,
gamma_slider_, chat_lines_slider_;
gui::button fullscreen_button_, turbo_button_, show_ai_moves_button_,
+ upload_log_button_,
show_grid_button_, show_lobby_joins_button_,
show_floating_labels_button_, turn_dialog_button_,
turn_bell_button_, show_team_colours_button_,
show_colour_cursors_button_,
show_haloing_button_, video_mode_button_, theme_button_,
hotkeys_button_, gamma_button_,
@@ -214,6 +216,7 @@
scroll_slider_(disp.video()), gamma_slider_(disp.video()),
chat_lines_slider_(disp.video()),
fullscreen_button_(disp.video(), _("Toggle Full Screen"),
gui::button::TYPE_CHECK),
turbo_button_(disp.video(), _("Accelerated Speed"),
gui::button::TYPE_CHECK),
+ upload_log_button_(disp.video(), _("Upload Log"),
gui::button::TYPE_CHECK),
show_ai_moves_button_(disp.video(), _("Skip AI Moves"),
gui::button::TYPE_CHECK),
show_grid_button_(disp.video(), _("Show Grid"),
gui::button::TYPE_CHECK),
show_lobby_joins_button_(disp.video(), _("Show lobby joins"),
gui::button::TYPE_CHECK),
@@ -289,6 +292,9 @@
turbo_button_.set_check(turbo());
turbo_button_.set_help_string(_("Make units move and fight faster"));
+ upload_log_button_.set_check(preferences::upload_log());
+ upload_log_button_.set_help_string(_("Help Wesnoth improve by sending
statistics on games to developers"));
+
show_ai_moves_button_.set_check(!show_ai_moves());
show_ai_moves_button_.set_help_string(_("Do not animate AI units
moving"));
@@ -344,6 +350,7 @@
SDL_Rect scroll_rect = { rect.x + scroll_label_.width(), ypos,
rect.w - scroll_label_.width() - border, 0 };
scroll_slider_.set_location(scroll_rect);
+ ypos += item_interline; upload_log_button_.set_location(rect.x, ypos);
ypos += item_interline; turbo_button_.set_location(rect.x, ypos);
ypos += item_interline; show_ai_moves_button_.set_location(rect.x,
ypos);
ypos += item_interline; turn_dialog_button_.set_location(rect.x, ypos);
@@ -413,6 +420,8 @@
{
if (turbo_button_.pressed())
set_turbo(turbo_button_.checked());
+ if (upload_log_button_.pressed())
+ preferences::set_upload_log(upload_log_button_.checked());
if (show_ai_moves_button_.pressed())
set_show_ai_moves(!show_ai_moves_button_.checked());
if (show_grid_button_.pressed())
@@ -546,6 +555,7 @@
scroll_label_.hide(hide_general);
scroll_slider_.hide(hide_general);
turbo_button_.hide(hide_general);
+ upload_log_button_.hide(hide_general);
show_ai_moves_button_.hide(hide_general);
turn_dialog_button_.hide(hide_general);
turn_bell_button_.hide(hide_general);
Index: preferences.hpp
===================================================================
--- preferences.hpp (revision 9227)
+++ preferences.hpp (working copy)
@@ -178,6 +178,11 @@
bool flip_time();
void set_flip_time(bool value);
+ bool upload_log();
+ bool upload_log_defined();
+ void set_upload_log(bool value);
+ void set_upload_experience_level(int level);
+
// Multiplayer functions
bool chat_timestamp();
void set_chat_timestamp(bool value);
--
ccontrol: http://ozlabs.org/~rusty/ccontrol