added Christoph's sleepy spiky, created a plant badguy using Grumbel's plant.xcf...
[supertux.git] / src / scripting / script_interpreter.cpp
index 64feaa7..6c3fce5 100644 (file)
@@ -1,10 +1,11 @@
 #include <config.h>
 
-#include "script_interpreter.h"
+#include "script_interpreter.hpp"
 
 #include <stdarg.h>
 #include <stdexcept>
 #include <sstream>
+#include <fstream>
 #include <sqstdio.h>
 #include <sqstdaux.h>
 #include <sqstdblob.h>
 #include <sqstdmath.h>
 #include <sqstdstring.h>
 
-#include "wrapper.h"
-#include "wrapper_util.h"
+#include "wrapper.hpp"
+#include "wrapper_util.hpp"
+#include "sector.hpp"
+#include "file_system.hpp"
+#include "game_session.hpp"
+#include "resources.hpp"
+#include "physfs/physfs_stream.hpp"
+#include "object/text_object.hpp"
+#include "object/scripted_object.hpp"
+#include "object/display_effect.hpp"
+#include "object/player.hpp"
+#include "scripting/sound.hpp"
+#include "scripting/scripted_object.hpp"
+#include "scripting/display_effect.hpp"
+#include "scripting/squirrel_error.hpp"
 
 static void printfunc(HSQUIRRELVM, const char* str, ...)
 {
@@ -25,7 +39,8 @@ static void printfunc(HSQUIRRELVM, const char* str, ...)
 
 ScriptInterpreter* ScriptInterpreter::_current = 0;
 
-ScriptInterpreter::ScriptInterpreter()
+ScriptInterpreter::ScriptInterpreter(const std::string& new_working_directory)
+  : working_directory(new_working_directory), sound(0), level(0), camera(0)
 {
   v = sq_open(1024);
   if(v == 0)
@@ -36,26 +51,67 @@ ScriptInterpreter::ScriptInterpreter()
   // register squirrel libs
   sq_pushroottable(v);
   if(sqstd_register_bloblib(v) < 0)
-    throw SquirrelError(v, "Couldn't register blob lib");
+    throw Scripting::SquirrelError(v, "Couldn't register blob lib");
   if(sqstd_register_iolib(v) < 0)
-    throw SquirrelError(v, "Couldn't register io lib");
+    throw Scripting::SquirrelError(v, "Couldn't register io lib");
   if(sqstd_register_systemlib(v) < 0)
-    throw SquirrelError(v, "Couldn't register system lib");
+    throw Scripting::SquirrelError(v, "Couldn't register system lib");
   if(sqstd_register_mathlib(v) < 0)
-    throw SquirrelError(v, "Couldn't register math lib");
+    throw Scripting::SquirrelError(v, "Couldn't register math lib");
   if(sqstd_register_stringlib(v) < 0)
-    throw SquirrelError(v, "Couldn't register string lib");
+    throw Scripting::SquirrelError(v, "Couldn't register string lib");
 
   // register print function
   sq_setprintfunc(v, printfunc);
   
   // register supertux API
-  register_functions(v, supertux_global_functions);
-  register_classes(v, supertux_classes);  
+  Scripting::register_supertux_wrapper(v);
+
+  // expose some "global" objects
+  sound = new Scripting::Sound();
+  expose_object(sound, "Sound");
+  
+  level = new Scripting::Level();
+  expose_object(level, "Level");
+}
+
+void
+ScriptInterpreter::register_sector(Sector* sector)
+{
+  // expose ScriptedObjects to the script
+  for(Sector::GameObjects::iterator i = sector->gameobjects.begin();
+      i != sector->gameobjects.end(); ++i) {
+    GameObject* object = *i;
+    Scripting::ScriptedObject* scripted_object
+      = dynamic_cast<Scripting::ScriptedObject*> (object);
+    if(!scripted_object)
+      continue;
+    
+    expose_object(scripted_object, scripted_object->get_name());
+  }
+  
+  expose_object(static_cast<Scripting::Player*> (sector->player), "Tux");
+  TextObject* text_object = new TextObject();
+  sector->add_object(text_object);
+  Scripting::Text* text = static_cast<Scripting::Text*> (text_object);
+  expose_object(text, "Text");
+  
+  DisplayEffect* display_effect = new DisplayEffect();
+  sector->add_object(display_effect);
+  Scripting::DisplayEffect* display_effect_api
+    = static_cast<Scripting::DisplayEffect*> (display_effect);
+  expose_object(display_effect_api, "DisplayEffect");
+
+  Scripting::Camera* camera = new Scripting::Camera(sector->camera);
+  expose_object(camera, "Camera");
 }
 
 ScriptInterpreter::~ScriptInterpreter()
 {
+  sq_close(v);
+  delete sound;
+  delete level;
+  delete camera;
 }
 
 static SQInteger squirrel_read_char(SQUserPointer file)
@@ -67,70 +123,88 @@ static SQInteger squirrel_read_char(SQUserPointer file)
   return c;
 }
 
-
 void
-ScriptInterpreter::load_script(std::istream& in, const std::string& sourcename)
+ScriptInterpreter::run_script(std::istream& in, const std::string& sourcename,
+        bool remove_when_terminated)
 {
   if(sq_compile(v, squirrel_read_char, &in, sourcename.c_str(), true) < 0)
-    throw SquirrelError(v, "Couldn't parse script");
-}
-
-void
-ScriptInterpreter::run_script()
-{
+    throw Scripting::SquirrelError(v, "Couldn't parse script");
   _current = this;
   sq_push(v, -2);
   if(sq_call(v, 1, false) < 0)
-    throw SquirrelError(v, "Couldn't start script");
+    throw Scripting::SquirrelError(v, "Couldn't start script");
   _current = 0;
+  if(sq_getvmstate(v) != SQ_VMSTATE_SUSPENDED) {
+    if(remove_when_terminated) {
+      remove_me();
+    }
+    // remove closure from stack
+    sq_pop(v, 1);
+  }
 }
 
 void
-ScriptInterpreter::expose_object(void* object, const std::string& name,
-                                 const std::string& type)
+ScriptInterpreter::set_wakeup_time(float seconds)
 {
-  // part1 of registration of the instance in the root table
-  sq_pushroottable(v);
-  sq_pushstring(v, name.c_str(), -1);
+  wakeup_timer.start(seconds);
+}
 
-  // resolve class name
-  sq_pushroottable(v);
-  sq_pushstring(v, type.c_str(), -1);
-  if(sq_get(v, -2) < 0) {
-    std::ostringstream msg;
-    msg << "Couldn't resolve squirrel type '" << type << "'.";
-    throw std::runtime_error(msg.str());
-  }
-  sq_remove(v, -2); // remove roottable
+void
+ScriptInterpreter::update(float )
+{
+  if(!wakeup_timer.check())
+    return;
   
-  // create an instance and set pointer to c++ object
-  if(sq_createinstance(v, -1) < 0 || sq_setinstanceup(v, -1, object)) {
-    std::ostringstream msg;
-    msg << "Couldn't setup squirrel instance for object '"
-        << name << "' of type '" << type << "'.";
-    throw SquirrelError(v, msg.str());
+  _current = this;
+  if(sq_wakeupvm(v, false, false) < 0)
+    throw Scripting::SquirrelError(v, "Couldn't resume script");
+  _current = 0;
+  if(sq_getvmstate(v) != SQ_VMSTATE_SUSPENDED) {
+    printf("script ended...\n");
+    remove_me();
   }
-  
-  sq_remove(v, -2); // remove class from stack
-  
-  // part2 of registration of the instance in the root table
-  if(sq_createslot(v, -3) < 0)
-    throw SquirrelError(v, "Couldn't register object in squirrel root table");    sq_pop(v, 1);
 }
 
 void
-ScriptInterpreter::suspend(float seconds)
+ScriptInterpreter::draw(DrawingContext& )
 {
-  resume_timer.start(seconds);
 }
 
 void
-ScriptInterpreter::update()
+ScriptInterpreter::add_script_object(Sector* sector, const std::string& name,
+    const std::string& script)
 {
-  if(resume_timer.check()) {
-    _current = this;
-    if(sq_wakeupvm(v, false, false) < 0)
-      throw SquirrelError(v, "Couldn't resume script");
-    _current = 0;
+  try {
+    std::string workdir = GameSession::current()->get_working_directory();
+    std::auto_ptr<ScriptInterpreter> interpreter(
+               new ScriptInterpreter(workdir));
+    interpreter->register_sector(sector);
+    
+    // load global default.nut file if it exists
+    //TODO: Load all .nut files from that directory
+    try {
+      std::string filename = "script/default.nut";
+      IFileStream in(filename);
+      interpreter->run_script(in, filename, false);
+    } catch(std::exception& e) {
+      // nothing
+    }
+
+    // load world-specific default.nut file if it exists
+    try {
+      std::string filename = workdir + "/default.nut";
+      IFileStream in(filename);
+      interpreter->run_script(in, filename, false);
+    } catch(std::exception& e) {
+      // nothing
+    }
+       
+    std::istringstream in(script);
+    interpreter->run_script(in, name);
+    sector->add_object(interpreter.release());
+  } catch(std::exception& e) {
+    std::cerr << "Couldn't start '" << name << "' script: " << e.what() << "\n";
   }
 }
+