From: Ricardo Cruz Date: Tue, 14 Sep 2004 22:26:23 +0000 (+0000) Subject: Implemented a statistics system. I believe this feature was originally requested... X-Git-Url: https://git.verplant.org/?a=commitdiff_plain;h=a2857397f9afd626046a2005d2614a14d9e11804;p=supertux.git Implemented a statistics system. I believe this feature was originally requested by Ryan and the aim is to provide more replay value. Currently, it just keeps track of score. In future, it could keep track of other things like: min number of jumps, max number of enemies killed, min number of shots, min time needed, etc. When a better value is reached after playing the level again, it is replaced in the old statistics. Worldmap is the one in charge for saving statistics. TODO: draw current score and other stats of the current level in world map. I am thinking in drawing it in the Jump'n Bump way, that is using fade on text. I had to use LispWriter when saving a slot. SVN-Revision: 1911 --- diff --git a/src/Makefile.am b/src/Makefile.am index 065679a04..06837def6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -14,7 +14,8 @@ supertux_SOURCES = badguy.cpp badguy.h bitmask.cpp bitmask.h camera.cpp \ scene.h special.cpp special.h supertux.cpp title.cpp title.h worldmap.cpp \ worldmap.h tile.h tile.cpp tile_manager.h tile_manager.cpp resources.h \ resources.cpp gameobjs.h gameobjs.cpp background.h background.cpp tilemap.h \ - tilemap.cpp serializable.h sector.cpp sector.h misc.h misc.cpp defines.h + tilemap.cpp serializable.h sector.cpp sector.h misc.h misc.cpp defines.h \ + statistics.cpp # EOF # INCLUDES = -I$(top_srcdir)/lib diff --git a/src/gameloop.cpp b/src/gameloop.cpp index 93abe3907..a8ab4fce7 100644 --- a/src/gameloop.cpp +++ b/src/gameloop.cpp @@ -61,6 +61,7 @@ #include "intro.h" #include "misc.h" #include "camera.h" +#include "statistics.h" GameSession* GameSession::current_ = 0; @@ -150,6 +151,8 @@ GameSession::restart_level() levelintro(); } + global_stats.reset(); + time_left.init(true); start_timers(); currentsector->play_music(LEVEL_MUSIC); @@ -785,7 +788,7 @@ GameSession::drawstatus(DrawingContext& context) { char str[60]; - snprintf(str, 60, " %d", player_status.score); + snprintf(str, 60, " %d", global_stats.get_points(SCORE_STAT)); context.draw_text(white_text, _("SCORE"), Vector(0, 0), LAYER_FOREGROUND1); context.draw_text(gold_text, str, Vector(96, 0), LAYER_FOREGROUND1); @@ -855,7 +858,7 @@ GameSession::drawresultscreen(void) context.draw_text_center(blue_text, _("Result:"), Vector(0, 200), LAYER_FOREGROUND1); - sprintf(str, _("SCORE: %d"), player_status.score); + sprintf(str, _("SCORE: %d"), global_stats.get_points(SCORE_STAT)); context.draw_text_center(gold_text, str, Vector(0, 224), LAYER_FOREGROUND1); sprintf(str, _("COINS: %d"), player_status.distros); diff --git a/src/gameloop.h b/src/gameloop.h index 1efa4a314..c72a3a1f9 100644 --- a/src/gameloop.h +++ b/src/gameloop.h @@ -86,6 +86,7 @@ private: // the sector and spawnpoint we shoudl spawn after this frame std::string newsector; std::string newspawnpoint; + public: enum ExitStatus { ES_NONE, ES_LEVEL_FINISHED, ES_GAME_OVER, ES_LEVEL_ABORT }; private: diff --git a/src/level.cpp b/src/level.cpp index 4aa704248..37ca6550e 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -156,4 +156,3 @@ Level::get_sector(const std::string& name) return i->second; } - diff --git a/src/scene.cpp b/src/scene.cpp index 238039e90..b2fdf45f3 100644 --- a/src/scene.cpp +++ b/src/scene.cpp @@ -25,8 +25,7 @@ PlayerStatus player_status; PlayerStatus::PlayerStatus() - : score(0), - distros(0), + : distros(0), lives(START_LIVES), bonus(NO_BONUS), score_multiplier(1) @@ -35,7 +34,6 @@ PlayerStatus::PlayerStatus() void PlayerStatus::reset() { - score = 0; distros = 0; lives = START_LIVES; bonus = NO_BONUS; diff --git a/src/scene.h b/src/scene.h index 2c34601a5..bb7e1d376 100644 --- a/src/scene.h +++ b/src/scene.h @@ -28,7 +28,6 @@ // Player stats struct PlayerStatus { - int score; int distros; int lives; enum BonusType { NO_BONUS, GROWUP_BONUS, FLOWER_BONUS }; diff --git a/src/sector.cpp b/src/sector.cpp index 300c31865..35faf64fe 100644 --- a/src/sector.cpp +++ b/src/sector.cpp @@ -40,6 +40,7 @@ #include "resources.h" #include "interactive_object.h" #include "door.h" +#include "statistics.h" Sector* Sector::_current = 0; @@ -653,7 +654,7 @@ Sector::collision_handler() void Sector::add_score(const Vector& pos, int s) { - player_status.score += s; + global_stats.add_points(SCORE_STAT, s); add_object(new FloatingScore(pos, s)); } @@ -779,9 +780,9 @@ Sector::trybreakbrick(const Vector& pos, bool small) counting_distros = false; solids->change_at(pos, tile->next_tile); } - + SoundManager::get()->play_sound(IDToSound(SND_DISTRO)); - player_status.score = player_status.score + SCORE_DISTRO; + global_stats.add_points(SCORE_STAT, SCORE_DISTRO); player_status.distros++; return true; } @@ -797,7 +798,7 @@ Sector::trybreakbrick(const Vector& pos, bool small) /* Get some score: */ SoundManager::get()->play_sound(IDToSound(SND_BRICK)); - player_status.score = player_status.score + SCORE_BRICK; + global_stats.add_points(SCORE_STAT, SCORE_BRICK); return true; } @@ -835,7 +836,7 @@ Sector::tryemptybox(const Vector& pos, Direction col_side) case 1: // Box with a distro! add_bouncy_distro(Vector(posx, posy)); SoundManager::get()->play_sound(IDToSound(SND_DISTRO)); - player_status.score = player_status.score + SCORE_DISTRO; + global_stats.add_points(SCORE_STAT, SCORE_DISTRO); player_status.distros++; break; @@ -900,7 +901,7 @@ Sector::trygrabdistro(const Vector& pos, int bounciness) (int)(pos.y / 32) * 32)); } - player_status.score = player_status.score + SCORE_DISTRO; + global_stats.add_points(SCORE_STAT, SCORE_DISTRO); player_status.distros++; } diff --git a/src/worldmap.cpp b/src/worldmap.cpp index a00932b0b..fb1bad6b9 100644 --- a/src/worldmap.cpp +++ b/src/worldmap.cpp @@ -28,6 +28,7 @@ #include "video/screen.h" #include "video/drawing_context.h" #include "utils/lispreader.h" +#include "utils/lispwriter.h" #include "special/frame_rate.h" #include "gameloop.h" #include "app/setup.h" @@ -512,7 +513,7 @@ WorldMap::load_map() reader.read_int("x", special_tile.x); reader.read_int("y", special_tile.y); - reader.read_string("level", special_tile.level_name, true); + reader.read_string("level", special_tile.level_name, false); special_tile.vertical_flip = false; reader.read_bool("vertical-flip", special_tile.vertical_flip); @@ -623,6 +624,18 @@ void WorldMap::get_level_title(SpecialTile& special_tile) delete reader; } +void WorldMap::calculate_total_stats() +{ + total_stats.reset(); + for(SpecialTiles::iterator i = special_tiles.begin(); i != special_tiles.end(); ++i) + { + if (!i->level_name.empty() && i->solved) + { + total_stats += i->statistics; + } + } +} + void WorldMap::on_escape_press() { @@ -831,6 +844,10 @@ WorldMap::update(float delta) bool old_level_state = special_tile->solved; special_tile->solved = true; + // deal with statistics + special_tile->statistics.merge(global_stats); + calculate_total_stats(); + if (session.get_current_sector()->player->got_power != session.get_current_sector()->player->NONE_POWER) player_status.bonus = PlayerStatus::FLOWER_BONUS; @@ -870,7 +887,6 @@ WorldMap::update(float delta) level_finished = false; /* In case the player's abort the special_tile, keep it using the old status. But the minimum lives and no bonus. */ - player_status.score = old_player_status.score; player_status.distros = old_player_status.distros; player_status.lives = std::min(old_player_status.lives, player_status.lives); player_status.bonus = player_status.NO_BONUS; @@ -880,9 +896,9 @@ WorldMap::update(float delta) { level_finished = false; /* draw an end screen */ - /* in the future, this should make a dialog a la SuperMario, asking + /* TODO: in the future, this should make a dialog a la SuperMario, asking if the player wants to restart the world map with no score and from - special_tile 1 */ + level 1 */ char str[80]; DrawingContext context; @@ -892,7 +908,7 @@ WorldMap::update(float delta) context.draw_text_center(blue_text, _("GAMEOVER"), Vector(0, 200), LAYER_FOREGROUND1); - sprintf(str, _("SCORE: %d"), player_status.score); + sprintf(str, _("SCORE: %d"), total_stats.get_points(SCORE_STAT)); context.draw_text_center(gold_text, str, Vector(0, 230), LAYER_FOREGROUND1); @@ -1047,7 +1063,7 @@ void WorldMap::draw_status(DrawingContext& context) { char str[80]; - sprintf(str, " %d", player_status.score); + sprintf(str, " %d", total_stats.get_points(SCORE_STAT)); context.draw_text(white_text, _("SCORE"), Vector(0, 0), LAYER_FOREGROUND1); context.draw_text(gold_text, str, Vector(96, 0), LAYER_FOREGROUND1); @@ -1174,38 +1190,60 @@ WorldMap::savegame(const std::string& filename) return; std::cout << "savegame: " << filename << std::endl; - std::ofstream out(filename.c_str()); - int nb_solved_levels = 0; + std::ofstream file(filename.c_str(), std::ios::out); + LispWriter* writer = new LispWriter(file); + + int nb_solved_levels = 0, total_levels = 0; for(SpecialTiles::iterator i = special_tiles.begin(); i != special_tiles.end(); ++i) { + if(!i->level_name.empty()) + ++total_levels; if (i->solved) ++nb_solved_levels; } + char nb_solved_levels_str[80], total_levels_str[80]; + sprintf(nb_solved_levels_str, "%d", nb_solved_levels); + sprintf(total_levels_str, "%d", total_levels); + + writer->write_comment("Worldmap save file"); + + writer->start_list("supertux-savegame"); + + writer->write_int("version", 1); + writer->write_string("title", std::string(name + " - " + nb_solved_levels_str + "/" + total_levels_str)); + writer->write_string("map", map_filename); + writer->write_int("lives", player_status.lives); + writer->write_int("distros", player_status.lives); + + writer->start_list("tux"); + + writer->write_float("x", tux->get_tile_pos().x); + writer->write_float("y", tux->get_tile_pos().y); + writer->write_string("back", direction_to_string(tux->back_direction)); + writer->write_string("bonus", bonus_to_string(player_status.bonus)); + + writer->end_list("tux"); + + writer->start_list("levels"); - out << "(supertux-savegame\n" - << " (version 1)\n" - << " (title \"" << name << " - " << nb_solved_levels << "/" << special_tiles.size() << "\")\n" - << " (map \"" << map_filename << "\")\n" - << " (lives " << player_status.lives << ")\n" - << " (score " << player_status.score << ")\n" - << " (distros " << player_status.distros << ")\n" - << " (tux (x " << tux->get_tile_pos().x << ") (y " << tux->get_tile_pos().y << ")\n" - << " (back \"" << direction_to_string(tux->back_direction) << "\")\n" - << " (bonus \"" << bonus_to_string(player_status.bonus) << "\"))\n" - << " (levels\n"; - for(SpecialTiles::iterator i = special_tiles.begin(); i != special_tiles.end(); ++i) { if (i->solved && !i->level_name.empty()) { - out << " (level (name \"" << i->level_name << "\")\n" - << " (solved #t))\n"; + writer->start_list("level"); + + writer->write_string("name", i->level_name); + writer->write_bool("solved", true); + i->statistics.write(*writer); + + writer->end_list("level"); } } - out << " )\n" - << " )\n\n;; EOF ;;" << std::endl; + writer->end_list("levels"); + + writer->end_list("supertux-savegame"); } void @@ -1245,7 +1283,6 @@ WorldMap::loadgame(const std::string& filename) load_map(); reader.read_int("lives", player_status.lives); - reader.read_int("score", player_status.score); reader.read_int("distros", player_status.distros); if (player_status.lives < 0) @@ -1283,13 +1320,17 @@ WorldMap::loadgame(const std::string& filename) bool solved = false; LispReader level_reader(data); - level_reader.read_string("name", name, true); + level_reader.read_string("name", name); level_reader.read_bool("solved", solved); for(SpecialTiles::iterator i = special_tiles.begin(); i != special_tiles.end(); ++i) { if (name == i->level_name) + { i->solved = solved; + i->statistics.parse(level_reader); + break; + } } } @@ -1298,6 +1339,8 @@ WorldMap::loadgame(const std::string& filename) } lisp_free(savegame); + + calculate_total_stats(); } void diff --git a/src/worldmap.h b/src/worldmap.h index cf5848757..d09deb244 100644 --- a/src/worldmap.h +++ b/src/worldmap.h @@ -26,6 +26,7 @@ #include "math/vector.h" #include "audio/musicref.h" #include "video/screen.h" +#include "statistics.h" extern Menu* worldmap_menu; @@ -159,6 +160,9 @@ public: std::string title; bool solved; + /** Statistics for level tiles */ + Statistics statistics; + /** Optional flags: */ /** Check if this level should be vertically flipped */ @@ -220,6 +224,12 @@ private: void get_level_title(SpecialTile& special_tile); void draw_status(DrawingContext& context); + + // to avoid calculating total stats all the time. This way only + // when need, it is calculated. + Statistics total_stats; + void calculate_total_stats(); + public: WorldMap(); ~WorldMap();