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