New grow and skid sounds from remaxim
[supertux.git] / src / world.cpp
index 71d60cf..a5ec785 100644 (file)
@@ -1,5 +1,5 @@
-//  $Id: level_subset.cpp 3118 2006-03-25 17:29:08Z sommer $
-// 
+//  $Id$
+//
 //  SuperTux
 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
 //
@@ -12,7 +12,7 @@
 //  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, write to the Free Software
 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "lisp/parser.hpp"
 #include "lisp/lisp.hpp"
 #include "physfs/physfs_stream.hpp"
-#include "script_manager.hpp"
-#include "scripting/wrapper_util.hpp"
-#include "msg.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "scripting/serialize.hpp"
+#include "log.hpp"
+#include "worldmap/worldmap.hpp"
+#include "mainloop.hpp"
+#include "player_status.hpp"
 
 static bool has_suffix(const std::string& data, const std::string& suffix)
 {
@@ -40,23 +43,51 @@ static bool has_suffix(const std::string& data, const std::string& suffix)
     return false;
 }
 
+World* World::current_ = NULL;
+
 World::World()
 {
   is_levelset = true;
   hide_from_contribs = false;
+  sq_resetobject(&world_thread);
 }
 
 World::~World()
 {
+  sq_release(Scripting::global_vm, &world_thread);
+  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
 World::load(const std::string& filename)
 {
   basedir = FileSystem::dirname(filename);
-  
+
   lisp::Parser parser;
-  std::auto_ptr<lisp::Lisp> root (parser.parse(filename));
+  const lisp::Lisp* root = parser.parse(filename);
 
   const lisp::Lisp* info = root->get_lisp("supertux-world");
   if(info == NULL)
@@ -70,16 +101,16 @@ World::load(const std::string& filename)
   info->get("title", title);
   info->get("description", description);
   info->get("levelset", is_levelset);
-  info->get_vector("levels", levels);
+  info->get("levels", levels);
   info->get("hide-from-contribs", hide_from_contribs);
 
   // Level info file doesn't define any levels, so read the
   // directory to see what we can find
-      
-  std::string path = basedir + "/";
+
+  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;
   }
 
@@ -94,15 +125,114 @@ World::load(const std::string& filename)
 void
 World::run()
 {
+  using namespace Scripting;
+
+  current_ = this;
+
+  // create new squirrel table for persistent game state
+  HSQUIRRELVM vm = Scripting::global_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";
-  std::cout << filename << std::endl;
-  if (!PHYSFS_exists(filename.c_str()))
-    return;
+  try {
+    IFileStream in(filename);
+
+    sq_release(global_vm, &world_thread);
+    world_thread = create_thread(global_vm);
+    compile_and_run(object_to_vm(world_thread), in, filename);
+  } catch(std::exception& ) {
+    // fallback: try to load worldmap worldmap.stwm
+    using namespace WorldMapNS;
+    main_loop->push_screen(new WorldMap(basedir + "worldmap.stwm"));
+  }
+}
+
+void
+World::save_state()
+{
+  using namespace Scripting;
+
+  lisp::Writer writer(savegame_filename);
 
-  IFileStream in(filename);
+  writer.start_list("supertux-savegame");
+  writer.write("version", 1);
 
-  HSQUIRRELVM vm = script_manager->create_thread();
-  Scripting::compile_and_run(vm, in, filename);
+  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("title", title.str());
+  }
+
+  writer.start_list("tux");
+  player_status->write(writer);
+  writer.end_list("tux");
+
+  writer.start_list("state");
+
+  sq_pushroottable(global_vm);
+  sq_pushstring(global_vm, "state", -1);
+  if(SQ_SUCCEEDED(sq_get(global_vm, -2))) {
+    Scripting::save_squirrel_table(global_vm, -1, writer);
+    sq_pop(global_vm, 1);
+  }
+  sq_pop(global_vm, 1);
+  writer.end_list("state");
+
+  writer.end_list("supertux-savegame");
+}
+
+void
+World::load_state()
+{
+  using namespace Scripting;
+
+  try {
+    lisp::Parser parser;
+    const 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");
+
+    sq_pushroottable(global_vm);
+    sq_pushstring(global_vm, "state", -1);
+    if(SQ_FAILED(sq_deleteslot(global_vm, -2, SQFalse)))
+      sq_pop(global_vm, 1);
+
+    sq_pushstring(global_vm, "state", -1);
+    sq_newtable(global_vm);
+    load_squirrel_table(global_vm, -1, state);
+    if(SQ_FAILED(sq_createslot(global_vm, -3)))
+      throw std::runtime_error("Couldn't create state table");
+    sq_pop(global_vm, 1);
+  } catch(std::exception& e) {
+    log_debug << "Couldn't load savegame: " << e.what() << std::endl;
+  }
 }
 
 const std::string&
@@ -117,3 +247,8 @@ World::get_num_levels() const
   return levels.size();
 }
 
+const std::string&
+World::get_basedir() const
+{
+  return basedir;
+}