91e3263566b8124593c390d793522fdff7f46adb
[supertux.git] / src / scripting / script_interpreter.cpp
1 #include <config.h>
2
3 #include "script_interpreter.hpp"
4
5 #include <stdarg.h>
6 #include <stdexcept>
7 #include <sstream>
8 #include <fstream>
9 #include <sqstdio.h>
10 #include <sqstdaux.h>
11 #include <sqstdblob.h>
12 #include <sqstdsystem.h>
13 #include <sqstdmath.h>
14 #include <sqstdstring.h>
15
16 #include "wrapper.hpp"
17 #include "wrapper_util.hpp"
18 #include "sector.hpp"
19 #include "file_system.hpp"
20 #include "game_session.hpp"
21 #include "resources.hpp"
22 #include "physfs/physfs_stream.hpp"
23 #include "object/text_object.hpp"
24 #include "object/scripted_object.hpp"
25 #include "object/display_effect.hpp"
26 #include "scripting/sound.hpp"
27 #include "scripting/scripted_object.hpp"
28 #include "scripting/display_effect.hpp"
29
30 static void printfunc(HSQUIRRELVM, const char* str, ...)
31 {
32   va_list arglist;
33   va_start(arglist, str);
34   vprintf(str, arglist);
35   va_end(arglist);
36 }
37
38 ScriptInterpreter* ScriptInterpreter::_current = 0;
39
40 ScriptInterpreter::ScriptInterpreter(const std::string& new_working_directory)
41   : working_directory(new_working_directory), sound(0), level(0)
42 {
43   v = sq_open(1024);
44   if(v == 0)
45     throw std::runtime_error("Couldn't initialize squirrel vm");
46
47   // register default error handlers
48   sqstd_seterrorhandlers(v);
49   // register squirrel libs
50   sq_pushroottable(v);
51   if(sqstd_register_bloblib(v) < 0)
52     throw SquirrelError(v, "Couldn't register blob lib");
53   if(sqstd_register_iolib(v) < 0)
54     throw SquirrelError(v, "Couldn't register io lib");
55   if(sqstd_register_systemlib(v) < 0)
56     throw SquirrelError(v, "Couldn't register system lib");
57   if(sqstd_register_mathlib(v) < 0)
58     throw SquirrelError(v, "Couldn't register math lib");
59   if(sqstd_register_stringlib(v) < 0)
60     throw SquirrelError(v, "Couldn't register string lib");
61
62   // register print function
63   sq_setprintfunc(v, printfunc);
64   
65   // register supertux API
66   SquirrelWrapper::register_supertux_wrapper(v);
67
68   // expose some "global" objects
69   sound = new Scripting::Sound();
70   expose_object(sound, "Sound");
71   
72   level = new Scripting::Level();
73   expose_object(level, "Level");
74 }
75
76 void
77 ScriptInterpreter::register_sector(Sector* sector)
78 {
79   // expose ScriptedObjects to the script
80   for(Sector::GameObjects::iterator i = sector->gameobjects.begin();
81       i != sector->gameobjects.end(); ++i) {
82     GameObject* object = *i;
83     Scripting::ScriptedObject* scripted_object
84       = dynamic_cast<Scripting::ScriptedObject*> (object);
85     if(!scripted_object)
86       continue;
87     
88     expose_object(scripted_object, scripted_object->get_name());
89   }
90   
91   TextObject* text_object = new TextObject();
92   sector->add_object(text_object);
93   Scripting::Text* text = static_cast<Scripting::Text*> (text_object);
94   expose_object(text, "Text");
95   
96   DisplayEffect* display_effect = new DisplayEffect();
97   sector->add_object(display_effect);
98   Scripting::DisplayEffect* display_effect_api
99     = static_cast<Scripting::DisplayEffect*> (display_effect);
100   expose_object(display_effect_api, "DisplayEffect");
101 }
102
103 ScriptInterpreter::~ScriptInterpreter()
104 {
105   sq_close(v);
106   delete sound;
107   delete level;
108 }
109
110 static SQInteger squirrel_read_char(SQUserPointer file)
111 {
112   std::istream* in = reinterpret_cast<std::istream*> (file);
113   char c = in->get();
114   if(in->eof())
115     return 0;    
116   return c;
117 }
118
119 void
120 ScriptInterpreter::run_script(std::istream& in, const std::string& sourcename,
121         bool remove_when_terminated)
122 {
123   printf("Stackbefore:\n");
124   print_squirrel_stack(v);
125   if(sq_compile(v, squirrel_read_char, &in, sourcename.c_str(), true) < 0)
126     throw SquirrelError(v, "Couldn't parse script");
127  
128   _current = this;
129   sq_push(v, -2);
130   if(sq_call(v, 1, false) < 0)
131     throw SquirrelError(v, "Couldn't start script");
132   _current = 0;
133   if(sq_getvmstate(v) != SQ_VMSTATE_SUSPENDED) {
134     if(remove_when_terminated) {
135       remove_me();
136     }
137     printf("ended.\n");
138     // remove closure from stack
139     sq_pop(v, 1);
140   }
141   printf("After:\n");
142   print_squirrel_stack(v);
143 }
144
145 void
146 ScriptInterpreter::set_wakeup_time(float seconds)
147 {
148   wakeup_timer.start(seconds);
149 }
150
151 void
152 ScriptInterpreter::update(float )
153 {
154   if(!wakeup_timer.check())
155     return;
156   
157   _current = this;
158   if(sq_wakeupvm(v, false, false) < 0)
159     throw SquirrelError(v, "Couldn't resume script");
160   _current = 0;
161   if(sq_getvmstate(v) != SQ_VMSTATE_SUSPENDED) {
162     printf("script ended...\n");
163     remove_me();
164   }
165 }
166
167 void
168 ScriptInterpreter::draw(DrawingContext& )
169 {
170 }
171
172 void
173 ScriptInterpreter::add_script_object(Sector* sector, const std::string& name,
174     const std::string& script)
175 {
176   try {
177     std::string workdir = GameSession::current()->get_working_directory();
178     std::auto_ptr<ScriptInterpreter> interpreter(
179                 new ScriptInterpreter(workdir));
180     interpreter->register_sector(sector);
181
182     // load default.nut file if it exists
183     try {
184       std::string filename = workdir + "/default.nut";
185       IFileStream in(filename);
186       interpreter->run_script(in, filename, false);
187     } catch(std::exception& e) {
188       // nothing
189     }
190         
191     std::istringstream in(script);
192     interpreter->run_script(in, name);
193     sector->add_object(interpreter.release());
194   } catch(std::exception& e) {
195     std::cerr << "Couldn't start '" << name << "' script: " << e.what() << "\n";
196   }
197 }
198