fade out console
[supertux.git] / src / world.cpp
index 7999097..b2bd524 100644 (file)
@@ -19,6 +19,7 @@
 //  02111-1307, USA.
 #include <config.h>
 
+#include <stddef.h>
 #include <physfs.h>
 #include <stdexcept>
 
 #include "physfs/physfs_stream.hpp"
 #include "script_manager.hpp"
 #include "scripting/wrapper_util.hpp"
-#include "msg.hpp"
+#include "scripting/serialize.hpp"
+#include "log.hpp"
+#include "worldmap.hpp"
+#include "mainloop.hpp"
 
 static bool has_suffix(const std::string& data, const std::string& suffix)
 {
@@ -39,6 +43,8 @@ static bool has_suffix(const std::string& data, const std::string& suffix)
     return false;
 }
 
+World* World::current_ = NULL;
+
 World::World()
 {
   is_levelset = true;
@@ -47,6 +53,30 @@ World::World()
 
 World::~World()
 {
+  if(current_ == this)
+    current_ = NULL;
+}
+
+void
+World::set_savegame_filename(const std::string& filename)
+{
+  this->savegame_filename = 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());
+  }
 }
 
 void
@@ -78,7 +108,7 @@ World::load(const std::string& filename)
   std::string path = basedir + "/";
   char** files = PHYSFS_enumerateFiles(path.c_str());
   if(!files) {
-    msg_warning << "Couldn't read subset dir '" << path << "'" << std::endl;
+    log_warning << "Couldn't read subset dir '" << path << "'" << std::endl;
     return;
   }
 
@@ -93,11 +123,110 @@ World::load(const std::string& filename)
 void
 World::run()
 {
+  current_ = this;
+  
+  // create new squirrel table for persisten game state
+  HSQUIRRELVM vm = ScriptManager::instance->get_vm();
+
+  sq_pushroottable(vm);
+  sq_pushstring(vm, "state", -1);
+  sq_newtable(vm);
+  if(SQ_FAILED(sq_createslot(vm, -3)))
+    throw Scripting::SquirrelError(vm, "Couldn't create state table");
+  sq_pop(vm, 1);
+
+  load_state();
+  
   std::string filename = basedir + "/world.nut";
-  IFileStream in(filename);
+  try {
+    IFileStream in(filename);
+
+    HSQUIRRELVM new_vm = ScriptManager::instance->create_thread();
+    Scripting::compile_and_run(new_vm, in, filename);
+  } catch(std::exception& e) {
+    using namespace WorldMapNS;
+    // fallback try to load worldmap
+    std::auto_ptr<WorldMap> worldmap (new WorldMap);
+    worldmap->loadmap(basedir + "worldmap.stwm");
+    main_loop->push_screen(worldmap.release());
+  }
+}
 
-  HSQUIRRELVM vm = script_manager->create_thread();
-  Scripting::compile_and_run(vm, in, filename);
+void
+World::save_state()
+{
+  lisp::Writer writer(savegame_filename);
+
+  writer.start_list("supertux-savegame");
+  writer.write_int("version", 1);
+  
+  using namespace WorldMapNS;
+  if(WorldMap::current() != NULL) {
+    std::ostringstream title;
+    title << WorldMap::current()->get_title();
+    title << " (" << WorldMap::current()->solved_level_count() 
+          << "/" << WorldMap::current()->level_count() << ")";
+    writer.write_string("title", title.str());
+  }
+
+  writer.start_list("tux");
+  player_status->write(writer);
+  writer.end_list("tux");
+
+  writer.start_list("state");
+  HSQUIRRELVM vm = ScriptManager::instance->get_vm();
+  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");
+}
+
+void
+World::load_state()
+{
+  try {
+    lisp::Parser parser;
+    std::auto_ptr<lisp::Lisp> root (parser.parse(savegame_filename));
+
+    const lisp::Lisp* lisp = root->get_lisp("supertux-savegame");
+    if(lisp == NULL)
+      throw std::runtime_error("file is not a supertux-savegame file");
+
+    int version = 1;
+    lisp->get("version", version);
+    if(version != 1)
+      throw std::runtime_error("incompatible savegame version");
+
+    const lisp::Lisp* tux = lisp->get_lisp("tux");
+    if(tux == NULL)
+      throw std::runtime_error("No tux section in savegame");
+    player_status->read(*tux);
+
+    const lisp::Lisp* state = lisp->get_lisp("state");
+    if(state == NULL)
+      throw std::runtime_error("No state section in savegame");
+    
+    HSQUIRRELVM vm = ScriptManager::instance->get_vm();
+    sq_pushroottable(vm);
+    sq_pushstring(vm, "state", -1);
+    if(SQ_FAILED(sq_deleteslot(vm, -2, SQFalse)))
+      sq_pop(vm, 1);
+    
+    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(std::exception& e) {
+    log_debug << "Couldn't load savegame: " << e.what() << std::endl;
+  }
 }
 
 const std::string&
@@ -112,3 +241,8 @@ World::get_num_levels() const
   return levels.size();
 }
 
+const std::string&
+World::get_basedir() const
+{
+  return basedir;
+}