- Change ScriptInterpreter to a gameobject, so we can now have several script
[supertux.git] / src / scripting / script_interpreter.cpp
1 #include <config.h>
2
3 #include "script_interpreter.h"
4
5 #include <stdarg.h>
6 #include <stdexcept>
7 #include <sstream>
8 #include <sqstdio.h>
9 #include <sqstdaux.h>
10 #include <sqstdblob.h>
11 #include <sqstdsystem.h>
12 #include <sqstdmath.h>
13 #include <sqstdstring.h>
14
15 #include "wrapper.h"
16 #include "wrapper_util.h"
17 #include "sector.h"
18 #include "object/text_object.h"
19 #include "object/scripted_object.h"
20 #include "scripting/sound.h"
21 #include "scripting/scripted_object.h"
22
23 static void printfunc(HSQUIRRELVM, const char* str, ...)
24 {
25   va_list arglist;
26   va_start(arglist, str);
27   vprintf(str, arglist);
28   va_end(arglist);
29 }
30
31 ScriptInterpreter* ScriptInterpreter::_current = 0;
32
33 ScriptInterpreter::ScriptInterpreter(Sector* sector)
34   : sound(0), level(0)
35 {
36   v = sq_open(1024);
37   if(v == 0)
38     throw std::runtime_error("Couldn't initialize squirrel vm");
39
40   // register default error handlers
41   sqstd_seterrorhandlers(v);
42   // register squirrel libs
43   sq_pushroottable(v);
44   if(sqstd_register_bloblib(v) < 0)
45     throw SquirrelError(v, "Couldn't register blob lib");
46   if(sqstd_register_iolib(v) < 0)
47     throw SquirrelError(v, "Couldn't register io lib");
48   if(sqstd_register_systemlib(v) < 0)
49     throw SquirrelError(v, "Couldn't register system lib");
50   if(sqstd_register_mathlib(v) < 0)
51     throw SquirrelError(v, "Couldn't register math lib");
52   if(sqstd_register_stringlib(v) < 0)
53     throw SquirrelError(v, "Couldn't register string lib");
54
55   // register print function
56   sq_setprintfunc(v, printfunc);
57   
58   // register supertux API
59   register_functions(v, supertux_global_functions);
60   register_classes(v, supertux_classes);  
61
62   // expose ScriptedObjects to the script
63   for(Sector::GameObjects::iterator i = sector->gameobjects.begin();
64       i != sector->gameobjects.end(); ++i) {
65     GameObject* object = *i;
66     Scripting::ScriptedObject* scripted_object
67       = dynamic_cast<Scripting::ScriptedObject*> (object);
68     if(!scripted_object)
69       continue;
70     
71     std::cout << "Exposing " << scripted_object->get_name() << "\n";
72     expose_object(scripted_object, scripted_object->get_name(), 
73         "ScriptedObject");
74   }
75   // expose some "global" objects
76   sound = new Scripting::Sound();
77   expose_object(sound, "Sound", "Sound");
78   level = new Scripting::Level();
79   expose_object(level, "Level", "Level");
80   TextObject* text_object = new TextObject();
81   sector->add_object(text_object);
82   Scripting::Text* text = static_cast<Scripting::Text*> (text_object);
83   expose_object(text, "Text", "Text");
84 }
85
86 ScriptInterpreter::~ScriptInterpreter()
87 {
88   sq_close(v);
89   delete sound;
90   delete level;
91 }
92
93 static SQInteger squirrel_read_char(SQUserPointer file)
94 {
95   std::istream* in = reinterpret_cast<std::istream*> (file);
96   char c = in->get();
97   if(in->eof())
98     return 0;    
99   return c;
100 }
101
102
103 void
104 ScriptInterpreter::load_script(std::istream& in, const std::string& sourcename)
105 {
106   if(sq_compile(v, squirrel_read_char, &in, sourcename.c_str(), true) < 0)
107     throw SquirrelError(v, "Couldn't parse script");
108 }
109
110 void
111 ScriptInterpreter::start_script()
112 {
113   _current = this;
114   sq_push(v, -2);
115   if(sq_call(v, 1, false) < 0)
116     throw SquirrelError(v, "Couldn't start script");
117   _current = 0;
118   if(sq_getvmstate(v) != SQ_VMSTATE_SUSPENDED) {
119     printf("script ended...\n");
120     remove_me();
121   }  
122 }
123
124 void
125 ScriptInterpreter::expose_object(void* object, const std::string& name,
126                                  const std::string& type)
127 {
128   // part1 of registration of the instance in the root table
129   sq_pushroottable(v);
130   sq_pushstring(v, name.c_str(), -1);
131
132   // resolve class name
133   sq_pushroottable(v);
134   sq_pushstring(v, type.c_str(), -1);
135   if(sq_get(v, -2) < 0) {
136     std::ostringstream msg;
137     msg << "Couldn't resolve squirrel type '" << type << "'.";
138     throw std::runtime_error(msg.str());
139   }
140   sq_remove(v, -2); // remove roottable
141   
142   // create an instance and set pointer to c++ object
143   if(sq_createinstance(v, -1) < 0 || sq_setinstanceup(v, -1, object)) {
144     std::ostringstream msg;
145     msg << "Couldn't setup squirrel instance for object '"
146         << name << "' of type '" << type << "'.";
147     throw SquirrelError(v, msg.str());
148   }
149   
150   sq_remove(v, -2); // remove class from stack
151   
152   // part2 of registration of the instance in the root table
153   if(sq_createslot(v, -3) < 0)
154     throw SquirrelError(v, "Couldn't register object in squirrel root table");    sq_pop(v, 1);
155 }
156
157 void
158 ScriptInterpreter::set_wakeup_time(float seconds)
159 {
160   wakeup_timer.start(seconds);
161 }
162
163 void
164 ScriptInterpreter::action(float )
165 {
166   if(!wakeup_timer.check())
167     return;
168   
169   _current = this;
170   if(sq_wakeupvm(v, false, false) < 0)
171     throw SquirrelError(v, "Couldn't resume script");
172   _current = 0;
173   if(sq_getvmstate(v) != SQ_VMSTATE_SUSPENDED) {
174     printf("script ended...\n");
175     remove_me();
176   }
177 }
178
179 void
180 ScriptInterpreter::draw(DrawingContext& )
181 {
182 }