Spiky, Snowball and Poison Ivy can move again
[supertux.git] / src / script_manager.cpp
1 //  $Id$
2 //
3 //  SuperTux
4 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
5 //
6 //  This program is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU General Public License
8 //  as published by the Free Software Foundation; either version 2
9 //  of the License, or (at your option) any later version.
10 //
11 //  This program is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //  GNU General Public License for more details.
15 //
16 //  You should have received a copy of the GNU General Public License
17 //  along with this program; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 //  02111-1307, USA.
20 #include <config.h>
21
22 #include "script_manager.hpp"
23
24 #include <stdarg.h>
25 #include <stdexcept>
26 #include <sstream>
27 #include <fstream>
28 #include <sqstdaux.h>
29 #include <sqstdblob.h>
30 #include <sqstdmath.h>
31 #include <sqstdstring.h>
32
33 #include "timer.hpp"
34 #include "console.hpp"
35 #include "log.hpp"
36 #include "scripting/wrapper.hpp"
37 #include "scripting/wrapper_util.hpp"
38 #include "scripting/squirrel_error.hpp"
39 #include "physfs/physfs_stream.hpp"
40
41 using namespace Scripting;
42
43 ScriptManager* ScriptManager::instance = NULL;
44
45 static void printfunc(HSQUIRRELVM, const char* str, ...)
46 {
47   char buf[4096];
48   va_list arglist; 
49   va_start(arglist, str); 
50   vsprintf(buf, str, arglist);
51   Console::output << (const char*) buf << std::flush;
52   va_end(arglist); 
53 }
54
55 ScriptManager::ScriptManager()
56   : parent(NULL)
57 {
58   vm = sq_open(64);
59   if(vm == 0)
60     throw std::runtime_error("Couldn't initialize squirrel vm");
61   sq_setforeignptr(vm, (SQUserPointer) this);
62
63   // register squirrel libs
64   sq_pushroottable(vm);
65   if(sqstd_register_bloblib(vm) < 0)
66     throw SquirrelError(vm, "Couldn't register blob lib");
67   if(sqstd_register_mathlib(vm) < 0)
68     throw SquirrelError(vm, "Couldn't register math lib");
69   if(sqstd_register_stringlib(vm) < 0)
70     throw SquirrelError(vm, "Couldn't register string lib");
71   // register supertux API
72   register_supertux_wrapper(vm);
73   sq_pop(vm, 1);
74
75   // register print function
76   sq_setprintfunc(vm, printfunc);
77   // register default error handlers
78   sqstd_seterrorhandlers(vm); 
79
80   // try to load default script
81   try {
82     std::string filename = "scripts/default.nut";
83     IFileStream stream(filename);
84     Scripting::compile_and_run(vm, stream, filename);
85   } catch(std::exception& e) {
86     log_warning << "Couldn't load default.nut: " << e.what() << std::endl;
87   }
88 }
89
90 ScriptManager::ScriptManager(ScriptManager* parent)
91 {
92   this->parent = parent;
93   vm = parent->vm;
94   parent->childs.push_back(this);
95 }
96
97 ScriptManager::~ScriptManager()
98 {
99   for(SquirrelVMs::iterator i = squirrel_vms.begin();
100       i != squirrel_vms.end(); ++i)
101     sq_release(vm, &(i->vm_obj));
102
103   if(parent != NULL) {
104     parent->childs.erase(
105         std::remove(parent->childs.begin(), parent->childs.end(), this),
106         parent->childs.end());
107   } else {
108     sq_close(vm);
109   }
110 }
111
112 HSQUIRRELVM
113 ScriptManager::create_thread(bool leave_thread_on_stack)
114 {
115   HSQUIRRELVM new_vm = sq_newthread(vm, 64);
116   if(new_vm == NULL)
117     throw SquirrelError(vm, "Couldn't create new VM");
118   sq_setforeignptr(new_vm, (SQUserPointer) this);
119
120   // retrieve reference to thread from stack and increase refcounter
121   HSQOBJECT vm_obj;
122   sq_resetobject(&vm_obj);
123   if(SQ_FAILED(sq_getstackobj(vm, -1, &vm_obj))) {
124     throw SquirrelError(vm, "Couldn't get coroutine vm from stack");
125   }
126   sq_addref(vm, &vm_obj);
127
128   if(!leave_thread_on_stack)
129     sq_pop(vm, 1);
130   
131   squirrel_vms.push_back(SquirrelVM(new_vm, vm_obj));
132
133   return new_vm;
134 }
135
136 void
137 ScriptManager::update()
138 {
139   for(SquirrelVMs::iterator i = squirrel_vms.begin(); i != squirrel_vms.end(); ) {
140     SquirrelVM& squirrel_vm = *i;
141     int vm_state = sq_getvmstate(squirrel_vm.vm);
142     
143     if(vm_state == SQ_VMSTATE_SUSPENDED && squirrel_vm.wakeup_time > 0 && game_time >= squirrel_vm.wakeup_time) {
144       squirrel_vm.waiting_for_events = WakeupData(NO_EVENT);
145       try {
146         if(SQ_FAILED(sq_wakeupvm(squirrel_vm.vm, false, false))) {
147           throw SquirrelError(squirrel_vm.vm, "Couldn't resume script");
148         }
149       } catch(std::exception& e) {
150         std::cerr << "Problem executing script: " << e.what() << "\n";
151         sq_release(vm, &squirrel_vm.vm_obj);
152         i = squirrel_vms.erase(i);
153         continue;
154       }
155     }
156         
157     if (vm_state != SQ_VMSTATE_SUSPENDED) {
158       sq_release(vm, &(squirrel_vm.vm_obj));
159       i = squirrel_vms.erase(i);
160     } else {
161       ++i;
162     }
163   }
164 }
165
166 void
167 ScriptManager::set_wakeup_event(HSQUIRRELVM vm, WakeupData event, float timeout)
168 {
169   assert(event.type >= 0 && event.type < WAKEUP_EVENT_COUNT);
170   // find the VM in the list and update it
171   for(SquirrelVMs::iterator i = squirrel_vms.begin();
172       i != squirrel_vms.end(); ++i) {
173     SquirrelVM& squirrel_vm = *i;
174     if(squirrel_vm.vm == vm) 
175       {
176         squirrel_vm.waiting_for_events = event;
177
178         if(timeout < 0) {
179           squirrel_vm.wakeup_time = -1;
180         } else {
181           squirrel_vm.wakeup_time = game_time + timeout;
182         }
183         return;
184       }
185   }
186 }
187
188 void
189 ScriptManager::fire_wakeup_event(WakeupData  event)
190 {
191   assert(event.type >= 0 && event.type < WAKEUP_EVENT_COUNT);
192   for(SquirrelVMs::iterator i = squirrel_vms.begin();
193       i != squirrel_vms.end(); ++i) {
194     SquirrelVM& vm = *i;
195     if(vm.waiting_for_events.type == event.type
196         && vm.waiting_for_events.type != NO_EVENT) {
197       vm.wakeup_time = game_time;
198       break;
199     }
200   }
201
202   for(std::vector<ScriptManager*>::iterator i = childs.begin();
203       i != childs.end(); ++i) {
204     ScriptManager* child = *i;
205     child->fire_wakeup_event(event);
206   }
207 }
208
209 void
210 ScriptManager::set_wakeup_event(HSQUIRRELVM vm, WakeupEvent event, float timeout)
211 {
212   set_wakeup_event(vm, WakeupData(event), timeout);
213 }
214
215 void
216 ScriptManager::fire_wakeup_event(WakeupEvent event)
217 {
218   fire_wakeup_event(WakeupData(event));
219 }
220
221 ScriptManager::SquirrelVM::SquirrelVM(HSQUIRRELVM arg_vm, HSQOBJECT arg_obj)
222   : vm(arg_vm), vm_obj(arg_obj)
223 {
224   waiting_for_events = WakeupData(NO_EVENT);
225   wakeup_time        = 0;
226 }
227