Let Tux bounce off badguys when he's invincible. This let's us bounce off guys like...
[supertux.git] / src / worldmap / worldmap.cpp
index 60158b8..8ae1a65 100644 (file)
@@ -33,8 +33,8 @@
 #include "gettext.hpp"
 #include "log.hpp"
 #include "mainloop.hpp"
+#include "shrinkfade.hpp"
 #include "video/surface.hpp"
-#include "video/screen.hpp"
 #include "video/drawing_context.hpp"
 #include "sprite/sprite_manager.hpp"
 #include "audio/sound_manager.hpp"
 #include "control/joystickkeyboardcontroller.hpp"
 #include "object/background.hpp"
 #include "object/tilemap.hpp"
-#include "script_manager.hpp"
 #include "options_menu.hpp"
 #include "scripting/squirrel_error.hpp"
-#include "scripting/wrapper_util.hpp"
+#include "scripting/squirrel_util.hpp"
 #include "worldmap/level.hpp"
 #include "worldmap/special_tile.hpp"
 #include "worldmap/tux.hpp"
@@ -151,16 +150,45 @@ WorldMap::WorldMap(const std::string& filename)
   worldmap_menu->add_entry(MNID_QUITWORLDMAP, _("Quit World"));
 
   load(filename);
+
+  // create a new squirrel table for the worldmap
+  using namespace Scripting;
+
+  sq_collectgarbage(global_vm);
+  sq_newtable(global_vm);
+  sq_pushroottable(global_vm);
+  if(SQ_FAILED(sq_setdelegate(global_vm, -2)))
+    throw Scripting::SquirrelError(global_vm, "Couldn't set worldmap_table delegate");
+
+  sq_resetobject(&worldmap_table);
+  if(SQ_FAILED(sq_getstackobj(global_vm, -1, &worldmap_table)))
+    throw Scripting::SquirrelError(global_vm, "Couldn't get table from stack");
+
+  sq_addref(global_vm, &worldmap_table);
+  sq_pop(global_vm, 1);              
 }
 
 WorldMap::~WorldMap()
 {
+  using namespace Scripting;
+
+  for(ScriptList::iterator i = scripts.begin();
+      i != scripts.end(); ++i) {
+    HSQOBJECT& object = *i;
+    sq_release(global_vm, &object);
+  }
+  sq_release(global_vm, &worldmap_table);
+
+  sq_collectgarbage(global_vm);
+  
   if(current_ == this)
     current_ = NULL;
 
   for(GameObjects::iterator i = game_objects.begin();
-      i != game_objects.end(); ++i)
-    delete *i;
+      i != game_objects.end(); ++i) {
+    GameObject* object = *i;
+    object->unref();
+  }
 
   for(SpawnPoints::iterator i = spawn_points.begin();
       i != spawn_points.end(); ++i) {
@@ -176,6 +204,7 @@ WorldMap::add_object(GameObject* object)
     solids = tilemap;
   }
 
+  object->ref();
   game_objects.push_back(object);
 }
 
@@ -207,21 +236,23 @@ WorldMap::load(const std::string& filename)
         add_object(new Background(*(iter.lisp())));
       } else if(iter.item() == "music") {
         iter.value()->get(music);
+      } else if(iter.item() == "init-script") {
+        iter.value()->get(init_script);
       } else if(iter.item() == "worldmap-spawnpoint") {
         SpawnPoint* sp = new SpawnPoint(iter.lisp());
         spawn_points.push_back(sp);
       } else if(iter.item() == "level") {
         LevelTile* level = new LevelTile(levels_path, iter.lisp());
         levels.push_back(level);
-        game_objects.push_back(level);
+        add_object(level);
       } else if(iter.item() == "special-tile") {
         SpecialTile* special_tile = new SpecialTile(iter.lisp());
         special_tiles.push_back(special_tile);
-        game_objects.push_back(special_tile);
+        add_object(special_tile);
       } else if(iter.item() == "sprite-change") {
         SpriteChange* sprite_change = new SpriteChange(iter.lisp());
         sprite_changes.push_back(sprite_change);
-        game_objects.push_back(sprite_change);
+        add_object(sprite_change);
       } else if(iter.item() == "name") {
         // skip
       } else {
@@ -403,10 +434,8 @@ WorldMap::finished_level(Level* gamelevel)
 
   if (level->extro_script != "") {
     try {
-      HSQUIRRELVM vm = ScriptManager::instance->create_thread();
-
       std::istringstream in(level->extro_script);
-      Scripting::compile_and_run(vm, in, "worldmap,extro_script");
+      run_script(in, "worldmap:extro_script");
     } catch(std::exception& e) {
       log_fatal << "Couldn't run level-extro-script: " << e.what() << std::endl;
     }
@@ -436,9 +465,8 @@ WorldMap::update(float delta)
   }
 
   // update GameObjects
-  for(GameObjects::iterator i = game_objects.begin();
-      i != game_objects.end(); ++i) {
-    GameObject* object = *i;
+  for(size_t i = 0; i < game_objects.size(); ++i) {
+    GameObject* object = game_objects[i];
     object->update(delta);
   }
 
@@ -447,7 +475,7 @@ WorldMap::update(float delta)
       i != game_objects.end(); ) {
     GameObject* object = *i;
     if(!object->is_valid()) {
-      delete object;
+      object->unref();
       i = game_objects.erase(i);
     } else {
       ++i;
@@ -502,13 +530,12 @@ WorldMap::update(float delta)
       }
 
       if (level->pos == tux->get_tile_pos()) {
-        // do a shriking fade to the level
-        shrink_fade(Vector((level->pos.x*32 + 16 + camera_offset.x),
-                           (level->pos.y*32 + 16 + camera_offset.y)), 500);
-
         try {
-          main_loop->push_screen(new GameSession(
-                levels_path + level->name, &level->statistics));
+          Vector shrinkpos = Vector(level->pos.x*32 + 16 - camera_offset.x,
+                                    level->pos.y*32 + 16 - camera_offset.y);
+          std::string levelfile = levels_path + level->name;
+          main_loop->push_screen(new GameSession(levelfile, &level->statistics),
+                                 new ShrinkFade(shrinkpos, 0.5));
         } catch(std::exception& e) {
           log_fatal << "Couldn't load level: " << e.what() << std::endl;
         }
@@ -639,6 +666,34 @@ WorldMap::setup()
 
   current_ = this;
   load_state();
+
+  // register worldmap_table as worldmap in scripting
+  using namespace Scripting;
+  
+  sq_pushroottable(global_vm);
+  sq_pushstring(global_vm, "worldmap", -1);
+  sq_pushobject(global_vm, worldmap_table);
+  if(SQ_FAILED(sq_createslot(global_vm, -3)))
+    throw SquirrelError(global_vm, "Couldn't set worldmap in roottable");
+  sq_pop(global_vm, 1);
+
+  if(init_script != "") {
+    std::istringstream in(init_script);
+    run_script(in, "WorldMap::init");
+  }
+}
+
+void
+WorldMap::leave()
+{
+  // remove worldmap_table from roottable
+  using namespace Scripting;
+
+  sq_pushroottable(global_vm);
+  sq_pushstring(global_vm, "worldmap", -1);
+  if(SQ_FAILED(sq_deleteslot(global_vm, -2, SQFalse)))
+    throw SquirrelError(global_vm, "Couldn't unset worldmap in roottable");
+  sq_pop(global_vm, 1);
 }
 
 static void store_float(HSQUIRRELVM vm, const char* name, float val)
@@ -738,7 +793,9 @@ static bool read_bool(HSQUIRRELVM vm, const char* name)
 void
 WorldMap::save_state()
 {
-  HSQUIRRELVM vm = ScriptManager::instance->get_vm();
+  using namespace Scripting;
+  
+  HSQUIRRELVM vm = global_vm;
   int oldtop = sq_gettop(vm);
 
   try {
@@ -812,7 +869,9 @@ WorldMap::save_state()
 void
 WorldMap::load_state()
 {
-  HSQUIRRELVM vm = ScriptManager::instance->get_vm();
+  using namespace Scripting;
+  
+  HSQUIRRELVM vm = global_vm;
   int oldtop = sq_gettop(vm);
  
   try {
@@ -888,5 +947,39 @@ WorldMap::solved_level_count()
 
   return count;
 }
-    
+
+HSQUIRRELVM
+WorldMap::run_script(std::istream& in, const std::string& sourcename)
+{
+  using namespace Scripting;
+
+  // garbage collect thread list
+  for(ScriptList::iterator i = scripts.begin();
+      i != scripts.end(); ) {
+    HSQOBJECT& object = *i;
+    HSQUIRRELVM vm = object_to_vm(object);
+
+    if(sq_getvmstate(vm) != SQ_VMSTATE_SUSPENDED) {
+      sq_release(global_vm, &object);
+      i = scripts.erase(i);
+      continue;
+    }
+
+    ++i;
+  }
+
+  HSQOBJECT object = create_thread(global_vm);
+  scripts.push_back(object);
+
+  HSQUIRRELVM vm = object_to_vm(object);
+
+  // set worldmap_table as roottable for the thread
+  sq_pushobject(vm, worldmap_table);
+  sq_setroottable(vm);
+
+  compile_and_run(vm, in, sourcename);
+
+  return vm;
+}
+   
 } // namespace WorldMapNS