Renamed WorldState to Savegame and implemented basic load/save for Worldmaps and...
authorIngo Ruhnke <grumbel@gmail.com>
Wed, 13 Aug 2014 19:50:47 +0000 (21:50 +0200)
committerIngo Ruhnke <grumbel@gmail.com>
Thu, 14 Aug 2014 00:59:02 +0000 (02:59 +0200)
Levelsets themselves are still broken and don't display properly in the menu
Worlds without any savegame will be displayed with a *NEW*

18 files changed:
src/supertux/game_manager.cpp
src/supertux/game_manager.hpp
src/supertux/game_session.cpp
src/supertux/game_session.hpp
src/supertux/main.cpp
src/supertux/menu/contrib_menu.cpp
src/supertux/savegame.cpp [new file with mode: 0644]
src/supertux/savegame.hpp [new file with mode: 0644]
src/supertux/sector.cpp
src/supertux/title_screen.cpp
src/supertux/title_screen.hpp
src/supertux/world.cpp
src/supertux/world.hpp
src/supertux/world_state.cpp [deleted file]
src/supertux/world_state.hpp [deleted file]
src/worldmap/tux.cpp
src/worldmap/worldmap.cpp
src/worldmap/worldmap.hpp

index baa82e2..79ffe6f 100644 (file)
 #include "supertux/screen_fade.hpp"
 #include "supertux/screen_manager.hpp"
 #include "supertux/world.hpp"
-#include "supertux/world_state.hpp"
+#include "supertux/savegame.hpp"
 #include "util/file_system.hpp"
 #include "util/log.hpp"
 #include "worldmap/worldmap.hpp"
 
 GameManager::GameManager() :
   m_world(),
-  m_world_state()
+  m_savegame()
 {
 }
 
@@ -46,15 +46,15 @@ GameManager::~GameManager()
 void
 GameManager::start_level(const std::string& level_filename)
 {
-  /*
+#ifdef GRUMBEL
   m_world = std::move(world);
-  m_world_state.reset(new WorldState);
-  m_world_state->load(m_world->get_savegame_filename());
+  m_savegame.reset(new Savegame);
+  m_savegame->load(m_world->get_savegame_filename());
 
   std::unique_ptr<Screen> screen(new GameSession(level_filename,
-                                                 &m_world_state));
+                                                 &m_savegame));
   g_screen_manager->push_screen(std::move(screen));
-  */
+#endif
 }
 
 void
@@ -63,13 +63,12 @@ GameManager::start_game(std::unique_ptr<World> world)
   try
   {
     m_world = std::move(world);
-    m_world_state.reset(new WorldState);
-
-    m_world_state->load(m_world->get_savegame_filename());
+    m_savegame.reset(new Savegame(m_world->get_savegame_filename()));
+    m_savegame->load();
 
     g_screen_manager->push_screen(std::unique_ptr<Screen>(
                                     new worldmap::WorldMap(m_world->get_worldmap_filename(),
-                                                           *m_world_state)));
+                                                           *m_savegame)));
   }
   catch(std::exception& e)
   {
index 5e21f88..e058793 100644 (file)
@@ -19,7 +19,7 @@
 
 #include <memory>
 
-#include "supertux/world_state.hpp"
+#include "supertux/savegame.hpp"
 #include "util/currenton.hpp"
 
 class World;
@@ -28,7 +28,7 @@ class GameManager : public Currenton<GameManager>
 {
 private:
   std::unique_ptr<World> m_world;
-  std::unique_ptr<WorldState> m_world_state;
+  std::unique_ptr<Savegame> m_savegame;
 
 public:
   GameManager();
index d0cc127..77af377 100644 (file)
 #include "supertux/screen_fade.hpp"
 #include "supertux/screen_manager.hpp"
 #include "supertux/sector.hpp"
-#include "supertux/world_state.hpp"
+#include "supertux/savegame.hpp"
 #include "util/file_system.hpp"
 #include "util/gettext.hpp"
 #include "worldmap/worldmap.hpp"
 
-GameSession::GameSession(const std::string& levelfile_, WorldState& world_state, Statistics* statistics) :
+GameSession::GameSession(const std::string& levelfile_, Savegame& savegame, Statistics* statistics) :
   level(),
   statistics_backdrop(Surface::create("images/engine/menu/score-backdrop.png")),
   scripts(),
@@ -61,7 +61,7 @@ GameSession::GameSession(const std::string& levelfile_, WorldState& world_state,
   newsector(),
   newspawnpoint(),
   best_level_statistics(statistics),
-  m_world_state(world_state),
+  m_savegame(savegame),
   capture_demo_stream(0),
   capture_file(),
   playback_demo_stream(0),
@@ -81,7 +81,7 @@ GameSession::GameSession(const std::string& levelfile_, WorldState& world_state,
 int
 GameSession::restart_level()
 {
-    PlayerStatus* currentStatus = m_world_state.get_player_status();
+    PlayerStatus* currentStatus = m_savegame.get_player_status();
     coins_at_start = currentStatus->coins;
     bonus_at_start = currentStatus->bonus;
     max_fire_bullets_at_start = currentStatus->max_fire_bullets;
@@ -257,7 +257,7 @@ GameSession::abort_level()
   MenuManager::instance().clear_menu_stack();
   g_screen_manager->pop_screen();
   currentsector->player->set_bonus(bonus_at_start);
-  PlayerStatus *currentStatus = m_world_state.get_player_status();
+  PlayerStatus *currentStatus = m_savegame.get_player_status();
   currentStatus->coins = coins_at_start;
   currentStatus->max_fire_bullets = max_fire_bullets_at_start;
   currentStatus->max_ice_bullets = max_ice_bullets_at_start;
@@ -575,7 +575,7 @@ GameSession::start_sequence(const std::string& sequencename)
 void
 GameSession::drawstatus(DrawingContext& context)
 {
-  m_world_state.get_player_status()->draw(context);
+  m_savegame.get_player_status()->draw(context);
 
   // draw level stats while end_sequence is running
   if (end_sequence) {
index 2d61b0e..725f5e0 100644 (file)
@@ -34,7 +34,7 @@ class Menu;
 class PlayerStatus;
 class Sector;
 class Statistics;
-class WorldState;
+class Savegame;
 
 /**
  * Screen that runs a Level, where Players run and jump through Sectors.
@@ -43,7 +43,7 @@ class GameSession : public Screen,
                     public Currenton<GameSession>
 {
 public:
-  GameSession(const std::string& levelfile, WorldState& world_state, Statistics* statistics = NULL);
+  GameSession(const std::string& levelfile, Savegame& savegame, Statistics* statistics = NULL);
   ~GameSession();
 
   void record_demo(const std::string& filename);
@@ -93,7 +93,7 @@ public:
    */
   void force_ghost_mode();
 
-  WorldState& get_world_state() { return m_world_state; }
+  Savegame& get_savegame() { return m_savegame; }
 
 private:
   void check_end_conditions();
@@ -134,7 +134,7 @@ private:
   std::string newspawnpoint;
 
   Statistics* best_level_statistics;
-  WorldState& m_world_state;
+  Savegame& m_savegame;
 
   std::ostream* capture_demo_stream;
   std::string capture_file;
index 75c7544..ddbac88 100644 (file)
@@ -358,7 +358,7 @@ Main::run(int argc, char** argv)
 
     timelog(0);
 
-    const std::unique_ptr<WorldState> default_world_state(new WorldState);
+    const std::unique_ptr<Savegame> default_savegame(new Savegame(std::string()));
 
     GameManager game_manager;
     g_screen_manager = new ScreenManager();
@@ -381,10 +381,10 @@ Main::run(int argc, char** argv)
          g_config->start_level.compare(g_config->start_level.size() - 5, 5, ".stwm") == 0) {
         g_screen_manager->push_screen(std::unique_ptr<Screen>(
                                         new worldmap::WorldMap(
-                                          FileSystem::basename(g_config->start_level), *default_world_state)));
+                                          FileSystem::basename(g_config->start_level), *default_savegame)));
       } else {
         std::unique_ptr<GameSession> session (
-          new GameSession(FileSystem::basename(g_config->start_level), *default_world_state));
+          new GameSession(FileSystem::basename(g_config->start_level), *default_savegame));
 
         g_config->random_seed =session->get_demo_random_seed(g_config->start_demo);
         init_rand();//initialise generator with seed from session
@@ -397,7 +397,7 @@ Main::run(int argc, char** argv)
         g_screen_manager->push_screen(std::move(session));
       }
     } else {
-      g_screen_manager->push_screen(std::unique_ptr<Screen>(new TitleScreen(*default_world_state)));
+      g_screen_manager->push_screen(std::unique_ptr<Screen>(new TitleScreen(*default_savegame)));
     }
 
     g_screen_manager->run(context);
index 8e2937f..cf862bd 100644 (file)
@@ -54,14 +54,69 @@ ContribMenu::ContribMenu() :
 
       if (!world->hide_from_contribs())
       {
-#ifdef GRUMBEL
-        world->load_state();
-#endif
+        Savegame savegame(world->get_savegame_filename());
+        savegame.load();
 
-        std::ostringstream title;
-        title << world->get_title(); // << " (" << world->get_num_solved_levels() << "/" << world->get_num_levels() << ")";
-        add_entry(i++, title.str());
-        m_contrib_worlds.push_back(std::move(world));
+        if (world->is_levelset())
+        {
+          int level_count = 0;
+          int solved_count = 0;
+
+          const auto& state = savegame.get_levelset_state(world->get_basedir());
+          for(const auto& level_state : state.level_states)
+          {
+            if (level_state.solved)
+            {
+              solved_count += 1;
+            }
+            level_count += 1;
+          }
+          
+          std::ostringstream title;
+          title << "[" << world->get_title() << "]";
+          if (level_count == 0)
+          {
+            title << " *NEW*";
+          }
+          else
+          {
+            title << " (" << solved_count << "/" << level_count << ")";
+          }
+          add_entry(i++, title.str());
+          m_contrib_worlds.push_back(std::move(world));
+        }
+        else if (world->is_worldmap())
+        {
+          int level_count = 0;
+          int solved_count = 0;
+
+          const auto& state = savegame.get_worldmap_state(world->get_worldmap_filename());
+          for(const auto& level_state : state.level_states)
+          {
+            if (level_state.solved)
+            {
+              solved_count += 1;
+            }
+            level_count += 1;
+          }
+          
+          std::ostringstream title;
+          title << world->get_title();
+          if (level_count == 0)
+          {
+            title << " *NEW*";
+          }
+          else
+          {
+            title << " (" << solved_count << "/" << level_count << ")";
+          }
+          add_entry(i++, title.str());
+          m_contrib_worlds.push_back(std::move(world));
+        }
+        else
+        {
+          log_warning << "unknown World type" << std::endl;
+        }
       }
     }
     catch(std::exception& e)
@@ -84,17 +139,16 @@ ContribMenu::check_menu()
   int index = check();
   if (index != -1)
   {
-    World* world = m_contrib_worlds[index].get();
+    // reload the World so that we have something that we can safely
+    // std::move() around without wreaking the ContribMenu
+    std::unique_ptr<World> world = World::load(m_contrib_worlds[index]->get_basedir());
     if (!world->is_levelset())
     {
-      // FIXME: not the most elegant of solutions to std::move() the
-      // World, but the ContribMenu should get destructed after this,
-      // so it might be ok
-      GameManager::current()->start_game(std::move(m_contrib_worlds[index]));
+      GameManager::current()->start_game(std::move(world));
     }
     else
     {
-      MenuManager::instance().push_menu(std::unique_ptr<Menu>(new ContribWorldMenu(std::move(m_contrib_worlds[index]))));
+      MenuManager::instance().push_menu(std::unique_ptr<Menu>(new ContribWorldMenu(std::move(world))));
     }
   }
 }
diff --git a/src/supertux/savegame.cpp b/src/supertux/savegame.cpp
new file mode 100644 (file)
index 0000000..ba2670c
--- /dev/null
@@ -0,0 +1,371 @@
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//                2014 Ingo Ruhnke <grumbel@gmx.de>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+#include "supertux/savegame.hpp"
+
+#include "lisp/lisp.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/writer.hpp"
+#include "physfs/ifile_streambuf.hpp"
+#include "scripting/serialize.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "supertux/player_status.hpp"
+#include "util/file_system.hpp"
+#include "util/log.hpp"
+#include "worldmap/worldmap.hpp"
+
+namespace {
+
+void get_table_entry(HSQUIRRELVM vm, const std::string& name)
+{
+  sq_pushstring(vm, name.c_str(), -1);
+  if(SQ_FAILED(sq_get(vm, -2)))
+  {
+    throw std::runtime_error("failed to get '" + name + "' table entry");
+  }
+  else
+  {
+    // successfully placed result on stack
+  } 
+}
+
+std::vector<std::string> get_table_keys(HSQUIRRELVM vm)
+{
+  std::vector<std::string> worlds;
+
+  sq_pushnull(vm);
+  while(SQ_SUCCEEDED(sq_next(vm, -2)))
+  {
+    //here -1 is the value and -2 is the key
+    const char* result;
+    if(SQ_FAILED(sq_getstring(vm, -2, &result))) 
+    {
+      std::ostringstream msg;
+      msg << "Couldn't get string value for key";
+      throw scripting::SquirrelError(vm, msg.str());
+    }
+    else
+    {
+      worlds.push_back(result);
+    }
+
+    // pops key and val before the next iteration
+    sq_pop(vm, 2);
+  }
+
+  return worlds;
+}
+
+std::vector<LevelState> get_level_states(HSQUIRRELVM vm)
+{
+  std::vector<LevelState> results;
+
+  sq_pushnull(vm);
+  while(SQ_SUCCEEDED(sq_next(vm, -2)))
+  {
+    //here -1 is the value and -2 is the key
+    const char* result;
+    if(SQ_FAILED(sq_getstring(vm, -2, &result))) 
+    {
+      std::ostringstream msg;
+      msg << "Couldn't get string value";
+      throw scripting::SquirrelError(vm, msg.str());
+    }
+    else
+    {
+      LevelState level_state;
+      level_state.filename = result;
+      scripting::get_bool(vm, "solved", level_state.solved);
+      scripting::get_bool(vm, "perfect", level_state.perfect);
+
+      results.push_back(level_state);
+    }
+
+    // pops key and val before the next iteration
+    sq_pop(vm, 2);
+  }
+
+  return results;
+}
+
+} // namespace
+
+Savegame::Savegame(const std::string& filename) :
+  m_filename(filename),
+  m_player_status(new PlayerStatus)
+{
+}
+
+Savegame::~Savegame()
+{
+}
+
+void
+Savegame::load()
+{
+  if (m_filename.empty())
+  {
+    log_debug << "no filename set for savegame, skipping load" << std::endl;
+    return;
+  }
+
+  if(!PHYSFS_exists(m_filename.c_str()))
+  {
+    log_info << m_filename << ": doesn't exist, not loading state" << std::endl;
+  }
+  else
+  {
+    log_debug << "loading savegame from " << m_filename << std::endl;
+
+    try
+    {
+      HSQUIRRELVM vm = scripting::global_vm;
+
+      lisp::Parser parser;
+      const lisp::Lisp* root = parser.parse(m_filename);
+
+      const lisp::Lisp* lisp = root->get_lisp("supertux-savegame");
+      if(lisp == NULL)
+      {
+        throw std::runtime_error("file is not a supertux-savegame file");
+      }
+      else
+      {
+        int version = 1;
+        lisp->get("version", version);
+        if(version != 1)
+        {
+          throw std::runtime_error("incompatible savegame version");
+        }
+        else
+        {
+          const lisp::Lisp* tux = lisp->get_lisp("tux");
+          if(tux == NULL)
+          {
+            throw std::runtime_error("No tux section in savegame");
+          }
+          {
+            m_player_status->read(*tux);
+          }
+
+          const lisp::Lisp* state = lisp->get_lisp("state");
+          if(state == NULL)
+          {
+            throw std::runtime_error("No state section in savegame");
+          }
+          else
+          {
+            // delete existing state table, if it exists
+            sq_pushroottable(vm);
+            sq_pushstring(vm, "state", -1);
+            if(SQ_FAILED(sq_deleteslot(vm, -2, SQFalse)))
+              sq_pop(vm, 1);
+
+            // create a new empty state table
+            sq_pushstring(vm, "state", -1);
+            sq_newtable(vm);
+            scripting::load_squirrel_table(vm, -1, *state);
+            if(SQ_FAILED(sq_createslot(vm, -3)))
+              throw std::runtime_error("Couldn't create state table");
+            sq_pop(vm, 1);
+          }
+        }
+      }
+    }
+    catch(const std::exception& e)
+    {
+      log_fatal << "Couldn't load savegame: " << e.what() << std::endl;
+    }
+  }
+}
+
+void
+Savegame::save()
+{
+  if (m_filename.empty())
+  {
+    log_debug << "no filename set for savegame, skipping save" << std::endl;
+    return;
+  }
+
+  log_debug << "saving savegame to " << m_filename << std::endl;
+
+  { // make sure the savegame directory exists
+    std::string dirname = FileSystem::dirname(m_filename);
+    if(!PHYSFS_exists(dirname.c_str()))
+    {
+      if(!PHYSFS_mkdir(dirname.c_str()))
+      {
+        std::ostringstream msg;
+        msg << "Couldn't create directory for savegames '"
+            << dirname << "': " <<PHYSFS_getLastError();
+        throw std::runtime_error(msg.str());
+      }
+    }
+
+    if(!PHYSFS_isDirectory(dirname.c_str()))
+    {
+      std::ostringstream msg;
+      msg << "Savegame path '" << dirname << "' is not a directory";
+      throw std::runtime_error(msg.str());
+    }
+  }
+
+  HSQUIRRELVM vm = scripting::global_vm;
+
+  lisp::Writer writer(m_filename);
+
+  writer.start_list("supertux-savegame");
+  writer.write("version", 1);
+
+  using namespace worldmap;
+  if(WorldMap::current() != NULL)
+  {
+    std::ostringstream title;
+    title << WorldMap::current()->get_title();
+    title << " (" << WorldMap::current()->solved_level_count()
+          << "/" << WorldMap::current()->level_count() << ")";
+    writer.write("title", title.str());
+  }
+
+  writer.start_list("tux");
+  m_player_status->write(writer);
+  writer.end_list("tux");
+
+  writer.start_list("state");
+
+  sq_pushroottable(vm);
+  sq_pushstring(vm, "state", -1);
+  if(SQ_SUCCEEDED(sq_get(vm, -2)))
+  {
+    scripting::save_squirrel_table(vm, -1, writer);
+    sq_pop(vm, 1);
+  }
+  sq_pop(vm, 1);
+  writer.end_list("state");
+
+  writer.end_list("supertux-savegame");
+}
+
+std::vector<std::string>
+Savegame::get_worldmaps()
+{
+  std::vector<std::string> worlds;
+
+  HSQUIRRELVM vm = scripting::global_vm;
+  int oldtop = sq_gettop(vm);
+
+  try
+  {
+    sq_pushroottable(vm);
+    get_table_entry(vm, "state");
+    get_table_entry(vm, "worlds");
+    worlds = get_table_keys(vm);
+  }
+  catch(const std::exception& err)
+  {
+    log_warning << err.what() << std::endl;
+  }
+
+  sq_settop(vm, oldtop);
+
+  return worlds;
+}
+
+WorldmapState
+Savegame::get_worldmap_state(const std::string& name)
+{
+  WorldmapState result;
+
+  HSQUIRRELVM vm = scripting::global_vm;
+  int oldtop = sq_gettop(vm);
+
+  try
+  {
+    sq_pushroottable(vm);
+    get_table_entry(vm, "state");
+    get_table_entry(vm, "worlds");
+    get_table_entry(vm, name);
+    get_table_entry(vm, "levels");
+
+    result.level_states = get_level_states(vm);
+  }
+  catch(const std::exception& err)
+  {
+    log_warning << err.what() << std::endl;
+  }
+
+  sq_settop(vm, oldtop);
+
+  return result;
+}
+
+std::vector<std::string>
+Savegame::get_levelsets()
+{
+  std::vector<std::string> results;
+
+  HSQUIRRELVM vm = scripting::global_vm;
+  int oldtop = sq_gettop(vm);
+
+  try
+  {
+    sq_pushroottable(vm);
+    get_table_entry(vm, "state");
+    get_table_entry(vm, "levelsets");
+    results = get_table_keys(vm);
+  }
+  catch(const std::exception& err)
+  {
+    log_warning << err.what() << std::endl;
+  }
+
+  sq_settop(vm, oldtop);
+
+  return results;
+}
+
+LevelsetState
+Savegame::get_levelset_state(const std::string& name)
+{
+  LevelsetState result;
+
+  HSQUIRRELVM vm = scripting::global_vm;
+  int oldtop = sq_gettop(vm);
+
+  try
+  {
+    sq_pushroottable(vm);
+    get_table_entry(vm, "state");
+    get_table_entry(vm, "levelsets");
+    get_table_entry(vm, name);
+    get_table_entry(vm, "levels");
+
+    result.level_states = get_level_states(vm);
+  }
+  catch(const std::exception& err)
+  {
+    log_warning << err.what() << std::endl;
+  }
+
+  sq_settop(vm, oldtop);
+
+  return result;
+}
+
+/* EOF */
diff --git a/src/supertux/savegame.hpp b/src/supertux/savegame.hpp
new file mode 100644 (file)
index 0000000..7c11a0d
--- /dev/null
@@ -0,0 +1,125 @@
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//                2014 Ingo Ruhnke <grumbel@gmx.de>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef HEADER_SUPERTUX_SUPERTUX_SAVEGAME_HPP
+#define HEADER_SUPERTUX_SUPERTUX_SAVEGAME_HPP
+
+#include <memory>
+#include <string>
+#include <vector>
+
+class PlayerStatus;
+
+struct LevelState
+{
+public:
+  LevelState() :
+    filename(),
+    solved(false),
+    perfect(false)
+  {}
+
+  std::string filename;
+  bool solved;
+  bool perfect;
+};
+
+struct LevelsetState
+{
+public:
+  LevelsetState() :
+    directory(),
+    level_states()
+  {}
+  std::string directory;
+  std::vector<LevelState> level_states;
+};
+
+struct WorldmapState
+{
+public:
+  WorldmapState() :
+    filename(),
+    level_states()
+  {}
+  std::string filename;
+  std::vector<LevelState> level_states;
+};
+
+/**
+(supertux-savegame
+  (version 1)
+  (title "Bonus Island II (0/28)")
+  (tux
+    (bonus "none")
+    (fireflowers 0)
+    (iceflowers 0)
+    (coins 110)
+  )
+  (state
+    ("levelsets"
+      ("levels/test/"
+        ("levels"
+          ("level10.stl"
+            (perfect #f)
+            (solved #f)
+          )
+    ("worlds"
+      ("levels/bonus2/worldmap.stwm"
+        ("tux" ....)
+        ("levels"
+          ("level10.stl"
+            (perfect #f)
+            (solved #f)
+          )
+          ("level28.stl"
+            (perfect #f)
+            (solved #f)
+          )
+ */
+class Savegame
+{
+private:
+  std::string m_filename;
+  std::unique_ptr<PlayerStatus> m_player_status;
+
+public:
+  Savegame(const std::string& filename);
+  ~Savegame();
+
+  /** Returns content of (tux ...) entry */
+  PlayerStatus* get_player_status() const { return m_player_status.get(); }
+
+  std::string get_title() const;
+
+  std::vector<std::string> get_levelsets();
+  LevelsetState get_levelset_state(const std::string& name);
+
+  std::vector<std::string> get_worldmaps();
+  WorldmapState get_worldmap_state(const std::string& name);
+
+  void save();
+  void load();
+
+private:
+  Savegame(const Savegame&) = delete;
+  Savegame& operator=(const Savegame&) = delete;
+};
+
+#endif
+
+/* EOF */
index 1cbffc6..333a737 100644 (file)
@@ -54,7 +54,7 @@
 #include "supertux/level.hpp"
 #include "supertux/object_factory.hpp"
 #include "supertux/player_status.hpp"
-#include "supertux/world_state.hpp"
+#include "supertux/savegame.hpp"
 #include "supertux/spawn_point.hpp"
 #include "supertux/tile.hpp"
 #include "trigger/sequence_trigger.hpp"
@@ -86,7 +86,7 @@ Sector::Sector(Level* parent) :
   camera(0),
   effect(0)
 {
-  add_object(new Player(GameSession::current()->get_world_state().get_player_status(), "Tux"));
+  add_object(new Player(GameSession::current()->get_savegame().get_player_status(), "Tux"));
   add_object(new DisplayEffect("Effect"));
   add_object(new TextObject("Text"));
 
index 8488bff..ec22c8b 100644 (file)
 #include <sstream>
 #include <version.h>
 
-TitleScreen::TitleScreen(WorldState& world_state) :
+TitleScreen::TitleScreen(Savegame& savegame) :
   frame(),
   controller(),
   titlesession(),
   copyright_text()
 {
   controller.reset(new CodeController());
-  titlesession.reset(new GameSession("levels/misc/menu.stl", world_state));
+  titlesession.reset(new GameSession("levels/misc/menu.stl", savegame));
 
   Player* player = titlesession->get_current_sector()->player;
   player->set_controller(controller.get());
index 40c6d86..65f68ea 100644 (file)
@@ -27,7 +27,7 @@ class ContribWorldMenu;
 class Menu;
 class PlayerStatus;
 class World;
-class WorldState;
+class Savegame;
 
 /**
  * Screen that displays the SuperTux logo, lets players start a new game, etc.
@@ -35,7 +35,7 @@ class WorldState;
 class TitleScreen : public Screen
 {
 public:
-  TitleScreen(WorldState& world_state);
+  TitleScreen(Savegame& savegame);
   virtual ~TitleScreen();
 
   virtual void setup();
index 02c5b15..b6734cc 100644 (file)
@@ -27,7 +27,7 @@
 #include "supertux/screen_fade.hpp"
 #include "supertux/screen_manager.hpp"
 #include "supertux/world.hpp"
-#include "supertux/world_state.hpp"
+#include "supertux/savegame.hpp"
 #include "util/file_system.hpp"
 #include "util/reader.hpp"
 #include "util/string_util.hpp"
@@ -60,12 +60,10 @@ World::World() :
   m_hide_from_contribs(false),
   m_is_levelset(true)
 {
-  std::cout << this << " World()" << std::endl;
 }
 
 World::~World()
 {
-  std::cout << this << " ~World()" << std::endl;
 }
 
 void
index 47bf2dc..6ce0ab1 100644 (file)
 #include <string>
 #include <vector>
 
-#include "util/currenton.hpp"
-#include "supertux/world_state.hpp"
+#include "supertux/savegame.hpp"
 
-class World : public Currenton<World>
+class World
 {
 private:
   World();
diff --git a/src/supertux/world_state.cpp b/src/supertux/world_state.cpp
deleted file mode 100644 (file)
index fc927d6..0000000
+++ /dev/null
@@ -1,243 +0,0 @@
-//  SuperTux
-//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//                2014 Ingo Ruhnke <grumbel@gmx.de>
-//
-//  This program is free software: you can redistribute it and/or modify
-//  it under the terms of the GNU General Public License as published by
-//  the Free Software Foundation, either version 3 of the License, or
-//  (at your option) any later version.
-//
-//  This program is distributed in the hope that it will be useful,
-//  but WITHOUT ANY WARRANTY; without even the implied warranty of
-//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//  GNU General Public License for more details.
-//
-//  You should have received a copy of the GNU General Public License
-//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-#include "supertux/world_state.hpp"
-
-#include "lisp/lisp.hpp"
-#include "lisp/parser.hpp"
-#include "lisp/writer.hpp"
-#include "physfs/ifile_streambuf.hpp"
-#include "scripting/serialize.hpp"
-#include "scripting/squirrel_util.hpp"
-#include "supertux/player_status.hpp"
-#include "util/file_system.hpp"
-#include "util/log.hpp"
-#include "worldmap/worldmap.hpp"
-
-WorldState::WorldState() :
-  m_player_status(new PlayerStatus)
-{
-}
-
-void
-WorldState::load(const std::string& filename)
-{
-  if(!PHYSFS_exists(filename.c_str()))
-  {
-    log_info << filename << ": doesn't exist, not loading state" << std::endl;
-  }
-  else
-  {
-    try
-    {
-      HSQUIRRELVM vm = scripting::global_vm;
-
-      lisp::Parser parser;
-      const lisp::Lisp* root = parser.parse(filename);
-
-      const lisp::Lisp* lisp = root->get_lisp("supertux-savegame");
-      if(lisp == NULL)
-      {
-        throw std::runtime_error("file is not a supertux-savegame file");
-      }
-      else
-      {
-        int version = 1;
-        lisp->get("version", version);
-        if(version != 1)
-        {
-          throw std::runtime_error("incompatible savegame version");
-        }
-        else
-        {
-          const lisp::Lisp* tux = lisp->get_lisp("tux");
-          if(tux == NULL)
-          {
-            throw std::runtime_error("No tux section in savegame");
-          }
-          {
-            m_player_status->read(*tux);
-          }
-
-          const lisp::Lisp* state = lisp->get_lisp("state");
-          if(state == NULL)
-          {
-            throw std::runtime_error("No state section in savegame");
-          }
-          else
-          {
-            // delete existing state table, if it exists
-            sq_pushroottable(vm);
-            sq_pushstring(vm, "state", -1);
-            if(SQ_FAILED(sq_deleteslot(vm, -2, SQFalse)))
-              sq_pop(vm, 1);
-
-            // create a new empty state table
-            sq_pushstring(vm, "state", -1);
-            sq_newtable(vm);
-            scripting::load_squirrel_table(vm, -1, *state);
-            if(SQ_FAILED(sq_createslot(vm, -3)))
-              throw std::runtime_error("Couldn't create state table");
-            sq_pop(vm, 1);
-          }
-        }
-      }
-    }
-    catch(const std::exception& e)
-    {
-      log_fatal << "Couldn't load savegame: " << e.what() << std::endl;
-    }
-  }
-}
-
-void
-WorldState::save(const std::string& filename)
-{
-  { // make sure the savegame directory exists
-    std::string dirname = FileSystem::dirname(filename);
-    if(!PHYSFS_exists(dirname.c_str()))
-    {
-      if(!PHYSFS_mkdir(dirname.c_str()))
-      {
-        std::ostringstream msg;
-        msg << "Couldn't create directory for savegames '"
-            << dirname << "': " <<PHYSFS_getLastError();
-        throw std::runtime_error(msg.str());
-      }
-    }
-
-    if(!PHYSFS_isDirectory(dirname.c_str()))
-    {
-      std::ostringstream msg;
-      msg << "Savegame path '" << dirname << "' is not a directory";
-      throw std::runtime_error(msg.str());
-    }
-  }
-
-  HSQUIRRELVM vm = scripting::global_vm;
-
-  lisp::Writer writer(filename);
-
-  writer.start_list("supertux-savegame");
-  writer.write("version", 1);
-
-  using namespace worldmap;
-  if(WorldMap::current() != NULL)
-  {
-    std::ostringstream title;
-    title << WorldMap::current()->get_title();
-    title << " (" << WorldMap::current()->solved_level_count()
-          << "/" << WorldMap::current()->level_count() << ")";
-    writer.write("title", title.str());
-  }
-
-  writer.start_list("tux");
-  m_player_status->write(writer);
-  writer.end_list("tux");
-
-  writer.start_list("state");
-
-  sq_pushroottable(vm);
-  sq_pushstring(vm, "state", -1);
-  if(SQ_SUCCEEDED(sq_get(vm, -2)))
-  {
-    scripting::save_squirrel_table(vm, -1, writer);
-    sq_pop(vm, 1);
-  }
-  sq_pop(vm, 1);
-  writer.end_list("state");
-
-  writer.end_list("supertux-savegame");
-}
-
-int
-WorldState::get_num_levels() const
-{
-#ifdef GRUMBEL
-#endif
-  return 5;
-}
-
-int
-WorldState::get_num_solved_levels() const
-{
-  return 3;
-#ifdef GRUMBEL
-  int num_solved_levels = 0;
-
-  HSQUIRRELVM vm = scripting::global_vm;
-  int oldtop = sq_gettop(vm);
-
-  sq_pushroottable(vm);
-  sq_pushstring(vm, "state", -1);
-  if(SQ_FAILED(sq_get(vm, -2)))
-  {
-    log_warning << "failed to get 'state' table" << std::endl;
-  }
-  else
-  {
-    sq_pushstring(vm, "worlds", -1);
-    if(SQ_FAILED(sq_get(vm, -2)))
-    {
-      log_warning << "failed to get 'state.worlds' table" << std::endl;
-    }
-    else
-    {
-      sq_pushstring(vm, m_worldmap_filename.c_str(), -1);
-      if(SQ_FAILED(sq_get(vm, -2)))
-      {
-        log_warning << "failed to get state.worlds['" << m_worldmap_filename << "']" << std::endl;
-      }
-      else
-      {
-        sq_pushstring(vm, "levels", -1);
-        if(SQ_FAILED(sq_get(vm, -2)))
-        {
-          log_warning << "failed to get state.worlds['" << m_worldmap_filename << "'].levels" << std::endl;
-        }
-        else
-        {
-          for(auto level : m_levels)
-          {
-            sq_pushstring(vm, level.c_str(), -1);
-            if(SQ_FAILED(sq_get(vm, -2)))
-            {
-              log_warning << "failed to get state.worlds['" << m_worldmap_filename << "'].levels['"
-                          << level << "']" << std::endl;
-            }
-            else
-            {
-              bool solved = scripting::read_bool(vm, "solved");
-              if (solved)
-              {
-                num_solved_levels += 1;
-              }
-              sq_pop(vm, 1);
-            }
-          }
-        }
-      }
-    }
-  }
-
-  sq_settop(vm, oldtop);
-
-  return num_solved_levels;
-#endif
-}
-
-/* EOF */
diff --git a/src/supertux/world_state.hpp b/src/supertux/world_state.hpp
deleted file mode 100644 (file)
index d78d9d5..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-//  SuperTux
-//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//                2014 Ingo Ruhnke <grumbel@gmx.de>
-//
-//  This program is free software: you can redistribute it and/or modify
-//  it under the terms of the GNU General Public License as published by
-//  the Free Software Foundation, either version 3 of the License, or
-//  (at your option) any later version.
-//
-//  This program is distributed in the hope that it will be useful,
-//  but WITHOUT ANY WARRANTY; without even the implied warranty of
-//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//  GNU General Public License for more details.
-//
-//  You should have received a copy of the GNU General Public License
-//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-#ifndef HEADER_SUPERTUX_SUPERTUX_WORLD_STATE_HPP
-#define HEADER_SUPERTUX_SUPERTUX_WORLD_STATE_HPP
-
-#include <string>
-#include <memory>
-
-class PlayerStatus;
-
-class WorldState
-{
-private:
-  std::unique_ptr<PlayerStatus> m_player_status;
-
-public:
-  WorldState();
-
-  PlayerStatus* get_player_status() const { return m_player_status.get(); }
-
-  int get_num_levels() const;
-  int get_num_solved_levels() const;
-
-  void save(const std::string& filename);
-  void load(const std::string& filename);
-
-private:
-  WorldState(const WorldState&) = delete;
-  WorldState& operator=(const WorldState&) = delete;
-};
-
-#endif
-
-/* EOF */
index eda5b4f..c76af1c 100644 (file)
@@ -21,7 +21,7 @@
 #include "sprite/sprite_manager.hpp"
 #include "supertux/globals.hpp"
 #include "supertux/player_status.hpp"
-#include "supertux/world_state.hpp"
+#include "supertux/savegame.hpp"
 #include "supertux/tile.hpp"
 #include "worldmap/level.hpp"
 #include "worldmap/tux.hpp"
@@ -60,7 +60,7 @@ Tux::~Tux()
 void
 Tux::draw(DrawingContext& context)
 {
-  switch (worldmap->get_world_state().get_player_status()->bonus) {
+  switch (worldmap->get_savegame().get_player_status()->bonus) {
     case GROWUP_BONUS:
       sprite->set_action(moving ? "large-walking" : "large-stop");
       break;
index 998695f..60dc779 100644 (file)
@@ -59,7 +59,7 @@
 #include "supertux/tile_manager.hpp"
 #include "supertux/tile_set.hpp"
 #include "supertux/world.hpp"
-#include "supertux/world_state.hpp"
+#include "supertux/savegame.hpp"
 #include "util/file_system.hpp"
 #include "util/gettext.hpp"
 #include "util/log.hpp"
@@ -78,9 +78,9 @@ namespace worldmap {
 
 WorldMap* WorldMap::current_ = NULL;
 
-WorldMap::WorldMap(const std::string& filename, WorldState& world_state, const std::string& force_spawnpoint) :
+WorldMap::WorldMap(const std::string& filename, Savegame& savegame, const std::string& force_spawnpoint) :
   tux(),
-  m_world_state(world_state),
+  m_savegame(savegame),
   tileset(NULL),
   free_tileset(false),
   camera_offset(),
@@ -240,7 +240,7 @@ void
 WorldMap::change(const std::string& filename, const std::string& force_spawnpoint)
 {
   g_screen_manager->pop_screen();
-  g_screen_manager->push_screen(std::unique_ptr<Screen>(new WorldMap(filename, m_world_state, force_spawnpoint)));
+  g_screen_manager->push_screen(std::unique_ptr<Screen>(new WorldMap(filename, m_savegame, force_spawnpoint)));
 }
 
 void
@@ -695,7 +695,7 @@ WorldMap::update(float delta)
           // update state and savegame
           save_state();
 
-          g_screen_manager->push_screen(std::unique_ptr<Screen>(new GameSession(levelfile, m_world_state, &level->statistics)),
+          g_screen_manager->push_screen(std::unique_ptr<Screen>(new GameSession(levelfile, m_savegame, &level->statistics)),
                                         std::unique_ptr<ScreenFade>(new ShrinkFade(shrinkpos, 1.0f)));
           in_level = true;
         } catch(std::exception& e) {
@@ -830,7 +830,7 @@ WorldMap::draw_status(DrawingContext& context)
   context.push_transform();
   context.set_translation(Vector(0, 0));
 
-  m_world_state.get_player_status()->draw(context);
+  m_savegame.get_player_status()->draw(context);
 
   if (!tux->is_moving()) {
     for(LevelTiles::iterator i = levels.begin(); i != levels.end(); ++i) {
@@ -1032,16 +1032,7 @@ WorldMap::save_state()
 
   sq_settop(vm, oldtop);
 
-  if (!World::current())
-  {
-    log_fatal << "no World::current(), so can't savegame" << std::endl;
-  }
-  else
-  {
-#ifdef GRUMBEL
-    m_world_state->save_state();
-#endif
-  }
+  m_savegame.save();
 }
 
 void
index db0749c..d0d15d5 100644 (file)
@@ -41,7 +41,7 @@ class GameObject;
 class PlayerStatus;
 class Sprite;
 class TileMap;
-class WorldState;
+class Savegame;
 
 namespace worldmap {
 
@@ -78,7 +78,7 @@ private:
 
   Tux* tux;
 
-  WorldState& m_world_state;
+  Savegame& m_savegame;
 
   TileSet *tileset;
   bool     free_tileset;
@@ -124,7 +124,7 @@ private:
   bool panning;
 
 public:
-  WorldMap(const std::string& filename, WorldState& world_state, const std::string& force_spawnpoint = "");
+  WorldMap(const std::string& filename, Savegame& savegame, const std::string& force_spawnpoint = "");
   ~WorldMap();
 
   void add_object(GameObject* object);
@@ -169,7 +169,7 @@ public:
   /** returns current Tux incarnation */
   Tux* get_tux() { return tux; }
 
-  WorldState& get_world_state() { return m_world_state; }
+  Savegame& get_savegame() { return m_savegame; }
 
   LevelTile* at_level();
   SpecialTile* at_special_tile();