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