- remove pointless leveltime from world1 levels
[supertux.git] / src / console.cpp
1 //  $Id: worldmap.cpp 3209 2006-04-02 22:19:22Z sommer $
2 //
3 //  SuperTux - Console
4 //  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.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  02111-1307, USA.
19 #include <config.h>
20
21 #include <iostream>
22 #include "console.hpp"
23 #include "video/drawing_context.hpp"
24 #include "video/surface.hpp"
25 #include "scripting/squirrel_error.hpp"
26 #include "scripting/wrapper_util.hpp"
27 #include "player_status.hpp"
28 #include "script_manager.hpp"
29 #include "main.hpp"
30 #include "resources.hpp"
31
32 /// speed (pixels/s) the console closes
33 static const float CLOSE_SPEED = 50;
34
35 Console::Console()
36   : backgroundOffset(0), height(0), offset(0), focused(false), stayOpen(0)
37 {
38   background.reset(new Surface("images/engine/console.png"));
39   background2.reset(new Surface("images/engine/console2.png"));
40 }
41
42 Console::~Console() 
43 {
44 }
45
46 void 
47 Console::flush(ConsoleStreamBuffer* buffer) 
48 {
49   if (buffer == &outputBuffer) {
50     std::string s = outputBuffer.str();
51     if ((s.length() > 0) && ((s[s.length()-1] == '\n') || (s[s.length()-1] == '\r'))) {
52       while ((s[s.length()-1] == '\n') || (s[s.length()-1] == '\r')) s.erase(s.length()-1);
53       addLine(s);
54       outputBuffer.str(std::string());
55     }
56   }
57   if (buffer == &inputBuffer) {
58     std::string s = inputBuffer.str();
59     if ((s.length() > 0) && ((s[s.length()-1] == '\n') || (s[s.length()-1] == '\r'))) {
60       while ((s[s.length()-1] == '\n') || (s[s.length()-1] == '\r')) s.erase(s.length()-1);
61       addLine("> "+s);
62       parse(s);
63       inputBuffer.str(std::string());
64     }
65   }
66 }
67
68 void
69 Console::execute_script(const std::string& command)
70 {
71   using namespace Scripting;
72
73   HSQUIRRELVM vm = ScriptManager::instance->get_vm();
74
75   if(command == "")
76     return;
77   
78   int oldtop = sq_gettop(vm); 
79   try {
80     if(SQ_FAILED(sq_compilebuffer(vm, command.c_str(), command.length(),
81                  "", SQTrue)))
82       throw SquirrelError(vm, "Couldn't compile command");
83
84     sq_pushroottable(vm);
85     if(SQ_FAILED(sq_call(vm, 1, SQTrue)))
86       throw SquirrelError(vm, "Problem while executing command");
87
88     if(sq_gettype(vm, -1) != OT_NULL)
89       addLine(squirrel2string(vm, -1));
90   } catch(std::exception& e) {
91     addLine(e.what());
92   }
93   int newtop = sq_gettop(vm);
94   if(newtop < oldtop) {
95     msg_fatal << "Script destroyed squirrel stack..." << std::endl;
96   } else {
97     sq_settop(vm, oldtop);
98   }
99 }
100
101 void
102 Console::backspace()
103 {
104   std::string s = inputBuffer.str();
105   if (s.length() > 0) {
106     s.erase(s.length()-1);
107     inputBuffer.str(s);
108     inputBuffer.pubseekoff(0, std::ios_base::end, std::ios_base::out);
109   }
110 }
111
112 void
113 Console::scroll(int numLines)
114 {
115   offset += numLines;
116   if (offset > 0) offset = 0;
117 }
118
119 void
120 Console::autocomplete()
121 {
122   std::string cmdPart = inputBuffer.str();
123   addLine("> "+cmdPart);
124
125   std::string cmdList = "";
126   int cmdListLen = 0;
127   for (std::map<std::string, std::list<ConsoleCommandReceiver*> >::iterator i = commands.begin(); i != commands.end(); i++) {
128     std::string cmdKnown = i->first;
129     if (cmdKnown.substr(0, cmdPart.length()) == cmdPart) {
130       if (cmdListLen > 0) cmdList = cmdList + ", ";
131       cmdList = cmdList + cmdKnown;
132       cmdListLen++;
133     }
134   }
135   if (cmdListLen == 0) addLine("No known command starts with \""+cmdPart+"\"");
136   if (cmdListLen == 1) {
137     inputBuffer.str(cmdList);
138     inputBuffer.pubseekoff(0, std::ios_base::end, std::ios_base::out);
139   }
140   if (cmdListLen > 1) addLine(cmdList);
141 }
142
143 void 
144 Console::addLine(std::string s) 
145 {
146   std::cerr << s << std::endl;
147   while (s.length() > 99) {
148     lines.push_front(s.substr(0, 99-3)+"...");
149     s = "..."+s.substr(99-3);
150   }
151   lines.push_front(s);
152   while (lines.size() >= 65535) lines.pop_back();
153   if (height < 64) {
154     if (height < 4+9) height=4+9;
155     height+=9;
156   }
157
158   if(stayOpen < 5)
159     stayOpen += 0.7;
160 }
161
162 void
163 Console::parse(std::string s) 
164 {
165   // make sure we actually have something to parse
166   if (s.length() == 0) return;
167         
168   // split line into list of args
169   std::vector<std::string> args;
170   size_t start = 0;
171   size_t end = 0;
172   while (1) {
173     start = s.find_first_not_of(" ,", end);
174     end = s.find_first_of(" ,", start);
175     if (start == s.npos) break;
176     args.push_back(s.substr(start, end-start));
177   }
178
179   // command is args[0]
180   if (args.size() == 0) return;
181   std::string command = args.front();
182   args.erase(args.begin());
183
184   // ignore if it's an internal command
185   if (consoleCommand(command,args)) return;
186
187   // look up registered ccr
188   std::map<std::string, std::list<ConsoleCommandReceiver*> >::iterator i = commands.find(command);
189   if ((i == commands.end()) || (i->second.size() == 0)) {
190     try {
191       execute_script(s);
192     } catch(std::exception& e) {
193       addLine(e.what());
194     }
195     return;
196   }
197
198   // send command to the most recently registered ccr
199   ConsoleCommandReceiver* ccr = i->second.front();
200   if (ccr->consoleCommand(command, args) != true) msg_warning << "Sent command to registered ccr, but command was unhandled" << std::endl;
201 }
202
203 bool
204 Console::consoleCommand(std::string command, std::vector<std::string> arguments) 
205 {
206   if (command == "ccrs") {
207     if (arguments.size() != 1) {
208       msg_info << "Usage: ccrs <command>" << std::endl;
209       return true;
210     }
211     std::map<std::string, std::list<ConsoleCommandReceiver*> >::iterator i = commands.find(arguments[0]);
212     if ((i == commands.end()) || (i->second.size() == 0)) {
213       msg_info << "unknown command: \"" << arguments[0] << "\"" << std::endl;
214       return true;
215     }
216
217     std::ostringstream ccr_list;
218     std::list<ConsoleCommandReceiver*> &ccrs = i->second;
219     std::list<ConsoleCommandReceiver*>::iterator j;
220     for (j = ccrs.begin(); j != ccrs.end(); j++) {
221       if (j != ccrs.begin()) ccr_list << ", ";
222       ccr_list << "[" << *j << "]";
223     }
224
225     msg_info << "registered ccrs for \"" << arguments[0] << "\": " << ccr_list.str() << std::endl;
226     return true;
227   }
228
229   return false;
230 }
231
232 bool
233 Console::hasFocus() 
234 {
235   return focused;
236 }
237
238 void
239 Console::show()
240 {
241   focused = true;
242   height = 256;
243 }
244
245 void 
246 Console::hide()
247 {
248   focused = false;
249   height = 0;
250   stayOpen = 0;
251
252   // clear input buffer
253   inputBuffer.str(std::string());
254 }
255
256 void 
257 Console::toggle()
258 {
259   if (Console::hasFocus()) {
260     Console::hide(); 
261   } 
262   else { 
263     Console::show();
264   }
265 }
266
267 void
268 Console::update(float elapsed_time)
269 {
270   if(stayOpen > 0) {
271     stayOpen -= elapsed_time;
272     if(stayOpen < 0)
273       stayOpen = 0;
274   } else if(!focused && height > 0) {
275     height -= elapsed_time * CLOSE_SPEED;
276     if(height < 0)
277       height = 0;
278   }
279 }
280
281 void 
282 Console::draw(DrawingContext& context)
283 {
284   if (height == 0)
285     return;
286
287   context.draw_surface(background2.get(), Vector(SCREEN_WIDTH/2 - background->get_width()/2 - background->get_width() + backgroundOffset, height - background->get_height()), LAYER_FOREGROUND1+1);
288   context.draw_surface(background2.get(), Vector(SCREEN_WIDTH/2 - background->get_width()/2 + backgroundOffset, height - background->get_height()), LAYER_FOREGROUND1+1);
289   context.draw_surface(background.get(), Vector(SCREEN_WIDTH/2 - background->get_width()/2, height - background->get_height()), LAYER_FOREGROUND1+1);
290   backgroundOffset+=10;
291   if (backgroundOffset > (int)background->get_width()) backgroundOffset -= (int)background->get_width();
292
293   int lineNo = 0;
294
295   if (focused) {
296     lineNo++;
297     float py = height-4-1*9;
298     context.draw_text(white_small_text, "> "+inputBuffer.str()+"_", Vector(4, py), LEFT_ALLIGN, LAYER_FOREGROUND1+1);
299   }
300
301   int skipLines = -offset;
302   for (std::list<std::string>::iterator i = lines.begin(); i != lines.end(); i++) {
303     if (skipLines-- > 0) continue;
304     lineNo++;
305     float py = height-4-lineNo*9;
306     if (py < -9) break;
307     context.draw_text(white_small_text, *i, Vector(4, py), LEFT_ALLIGN, LAYER_FOREGROUND1+1);
308   }
309 }
310
311 void 
312 Console::registerCommand(std::string command, ConsoleCommandReceiver* ccr)
313 {
314   commands[command].push_front(ccr);
315 }
316
317 void 
318 Console::unregisterCommand(std::string command, ConsoleCommandReceiver* ccr)
319 {
320   std::map<std::string, std::list<ConsoleCommandReceiver*> >::iterator i = commands.find(command);
321   if ((i == commands.end()) || (i->second.size() == 0)) {
322     msg_warning << "Command \"" << command << "\" not associated with a command receiver. Not dissociated." << std::endl;
323     return;
324   }
325   std::list<ConsoleCommandReceiver*>::iterator j = find(i->second.begin(), i->second.end(), ccr);
326   if (j == i->second.end()) {
327     msg_warning << "Command \"" << command << "\" not associated with given command receiver. Not dissociated." << std::endl;
328     return;
329   }
330   i->second.erase(j);
331 }
332
333 void 
334 Console::unregisterCommands(ConsoleCommandReceiver* ccr)
335 {
336   for (std::map<std::string, std::list<ConsoleCommandReceiver*> >::iterator i = commands.begin(); i != commands.end(); i++) {
337     std::list<ConsoleCommandReceiver*> &ccrs = i->second;
338     std::list<ConsoleCommandReceiver*>::iterator j;
339     while ((j = find(ccrs.begin(), ccrs.end(), ccr)) != ccrs.end()) {
340       ccrs.erase(j);
341     }
342   }
343 }
344
345 Console* Console::instance = NULL;
346 ConsoleStreamBuffer Console::inputBuffer;
347 ConsoleStreamBuffer Console::outputBuffer;
348 std::ostream Console::input(&Console::inputBuffer);
349 std::ostream Console::output(&Console::outputBuffer);
350