several bugfixes to squirrel serialisation code, serializer/load worldmap state from...
[supertux.git] / src / worldmap.cpp
1 //  $Id$
2 //
3 //  SuperTux -  A Jump'n Run
4 //  Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
5 //  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
6 //
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License
9 //  as published by the Free Software Foundation; either version 2
10 //  of the License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //  GNU General Public License for more details.
16 //
17 //  You should have received a copy of the GNU General Public License
18 //  along with this program; if not, write to the Free Software
19 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 #include <config.h>
21
22 #include <iostream>
23 #include <fstream>
24 #include <vector>
25 #include <cassert>
26 #include <stdexcept>
27 #include <sstream>
28 #include <unistd.h>
29 #include <physfs.h>
30
31 #include "worldmap.hpp"
32
33 #include "gettext.hpp"
34 #include "msg.hpp"
35 #include "mainloop.hpp"
36 #include "video/surface.hpp"
37 #include "video/screen.hpp"
38 #include "video/drawing_context.hpp"
39 #include "sprite/sprite_manager.hpp"
40 #include "audio/sound_manager.hpp"
41 #include "lisp/parser.hpp"
42 #include "lisp/lisp.hpp"
43 #include "lisp/list_iterator.hpp"
44 #include "lisp/writer.hpp"
45 #include "game_session.hpp"
46 #include "sector.hpp"
47 #include "worldmap.hpp"
48 #include "resources.hpp"
49 #include "misc.hpp"
50 #include "msg.hpp"
51 #include "player_status.hpp"
52 #include "textscroller.hpp"
53 #include "main.hpp"
54 #include "spawn_point.hpp"
55 #include "file_system.hpp"
56 #include "gui/menu.hpp"
57 #include "gui/mousecursor.hpp"
58 #include "control/joystickkeyboardcontroller.hpp"
59 #include "object/background.hpp"
60 #include "object/tilemap.hpp"
61 #include "scripting/squirrel_error.hpp"
62 #include "scripting/wrapper_util.hpp"
63
64 Menu* worldmap_menu  = 0;
65
66 static const float TUXSPEED = 200;
67 static const float map_message_TIME = 2.8;
68
69 namespace WorldMapNS {
70
71 WorldMap* WorldMap::current_ = NULL;
72
73 Direction reverse_dir(Direction direction)
74 {
75   switch(direction)
76     {
77     case D_WEST:
78       return D_EAST;
79     case D_EAST:
80       return D_WEST;
81     case D_NORTH:
82       return D_SOUTH;
83     case D_SOUTH:
84       return D_NORTH;
85     case D_NONE:
86       return D_NONE;
87     }
88   return D_NONE;
89 }
90
91 std::string
92 direction_to_string(Direction direction)
93 {
94   switch(direction)
95     {
96     case D_WEST:
97       return "west";
98     case D_EAST:
99       return "east";
100     case D_NORTH:
101       return "north";
102     case D_SOUTH:
103       return "south";
104     default:
105       return "none";
106     }
107 }
108
109 Direction
110 string_to_direction(const std::string& directory)
111 {
112   if (directory == "west")
113     return D_WEST;
114   else if (directory == "east")
115     return D_EAST;
116   else if (directory == "north")
117     return D_NORTH;
118   else if (directory == "south")
119     return D_SOUTH;
120   else
121     return D_NONE;
122 }
123
124 //---------------------------------------------------------------------------
125
126 Tux::Tux(WorldMap* worldmap_)
127   : worldmap(worldmap_)
128 {
129   tux_sprite = sprite_manager->create("images/worldmap/common/tux.sprite");
130   
131   offset = 0;
132   moving = false;
133   direction = D_NONE;
134   input_direction = D_NONE;
135 }
136
137 Tux::~Tux()
138 {
139   delete tux_sprite;
140 }
141
142 void
143 Tux::draw(DrawingContext& context)
144 {
145   switch (player_status->bonus) {
146     case GROWUP_BONUS:
147       tux_sprite->set_action(moving ? "large-walking" : "large-stop");
148       break;
149     case FIRE_BONUS:
150       tux_sprite->set_action(moving ? "fire-walking" : "fire-stop");
151       break;
152     case NO_BONUS:
153       tux_sprite->set_action(moving ? "small-walking" : "small-stop");
154       break;
155     default:
156       msg_debug << "Bonus type not handled in worldmap." << std::endl;
157       tux_sprite->set_action("large-stop");
158       break;
159   }
160
161   tux_sprite->draw(context, get_pos(), LAYER_OBJECTS);
162 }
163
164
165 Vector
166 Tux::get_pos()
167 {
168   float x = tile_pos.x * 32;
169   float y = tile_pos.y * 32;
170
171   switch(direction)
172     {
173     case D_WEST:
174       x -= offset - 32;
175       break;
176     case D_EAST:
177       x += offset - 32;
178       break;
179     case D_NORTH:
180       y -= offset - 32;
181       break;
182     case D_SOUTH:
183       y += offset - 32;
184       break;
185     case D_NONE:
186       break;
187     }
188   
189   return Vector(x, y);
190 }
191
192 void
193 Tux::stop()
194 {
195   offset = 0;
196   direction = D_NONE;
197   input_direction = D_NONE;
198   moving = false;
199 }
200
201 void
202 Tux::set_direction(Direction dir)
203 {
204   input_direction = dir;
205 }
206
207 void 
208 Tux::tryStartWalking() 
209 {
210   if (moving) return;
211   if (input_direction == D_NONE) return;
212
213   WorldMap::Level* level = worldmap->at_level();
214
215   // We got a new direction, so lets start walking when possible
216   Vector next_tile;
217   if ((!level || level->solved) && worldmap->path_ok(input_direction, tile_pos, &next_tile))
218   {
219     tile_pos = next_tile;
220     moving = true;
221     direction = input_direction;
222     back_direction = reverse_dir(direction);
223   }
224   else if (input_direction == back_direction)
225   {
226     moving = true;
227     direction = input_direction;
228     tile_pos = worldmap->get_next_tile(tile_pos, direction);
229     back_direction = reverse_dir(direction);
230   }
231
232 }
233
234 bool 
235 Tux::canWalk(const Tile* tile, Direction dir)
236 {
237   return ((tile->getData() & Tile::WORLDMAP_NORTH && dir == D_NORTH) ||
238           (tile->getData() & Tile::WORLDMAP_SOUTH && dir == D_SOUTH) ||
239           (tile->getData() & Tile::WORLDMAP_EAST && dir == D_EAST) ||
240           (tile->getData() & Tile::WORLDMAP_WEST && dir == D_WEST));
241 }
242
243 void 
244 Tux::tryContinueWalking(float elapsed_time)
245 {
246   if (!moving) return;
247
248   // Let tux walk
249   offset += TUXSPEED * elapsed_time;
250
251   // Do nothing if we have not yet reached the next tile
252   if (offset <= 32) return;
253
254   offset -= 32;
255
256   // if this is a special_tile with passive_message, display it
257   WorldMap::SpecialTile* special_tile = worldmap->at_special_tile();
258   if(special_tile && special_tile->passive_message)
259   {  
260     // direction and the apply_action_ are opposites, since they "see"
261     // directions in a different way
262     if((direction == D_NORTH && special_tile->apply_action_south) ||
263                     (direction == D_SOUTH && special_tile->apply_action_north) ||
264                     (direction == D_WEST && special_tile->apply_action_east) ||
265                     (direction == D_EAST && special_tile->apply_action_west))
266     {
267       worldmap->passive_message = special_tile->map_message;
268       worldmap->passive_message_timer.start(map_message_TIME);
269     }
270   }
271
272   // stop if we reached a level, a WORLDMAP_STOP tile or a special tile without a passive_message
273   if ((worldmap->at_level()) || (worldmap->at(tile_pos)->getData() & Tile::WORLDMAP_STOP) || (special_tile && !special_tile->passive_message))
274   {
275     if(special_tile && !special_tile->map_message.empty() && !special_tile->passive_message) worldmap->passive_message_timer.start(0);
276     stop();
277     return;
278   }
279
280   // if user wants to change direction, try changing, else guess the direction in which to walk next
281   const Tile* tile = worldmap->at(tile_pos);
282   if (direction != input_direction)
283   { 
284     if(canWalk(tile, input_direction))
285     {  
286       direction = input_direction;
287       back_direction = reverse_dir(direction);
288     }
289   }
290   else
291   {
292     Direction dir = D_NONE;
293     if (tile->getData() & Tile::WORLDMAP_NORTH && back_direction != D_NORTH) dir = D_NORTH;
294     else if (tile->getData() & Tile::WORLDMAP_SOUTH && back_direction != D_SOUTH) dir = D_SOUTH;
295     else if (tile->getData() & Tile::WORLDMAP_EAST && back_direction != D_EAST) dir = D_EAST;
296     else if (tile->getData() & Tile::WORLDMAP_WEST && back_direction != D_WEST) dir = D_WEST;
297
298     if (dir == D_NONE) 
299     {
300       // Should never be reached if tiledata is good
301       msg_warning << "Could not determine where to walk next" << std::endl;
302       stop();
303       return;
304     }
305
306     direction = dir;
307     input_direction = direction;
308     back_direction = reverse_dir(direction);
309   }
310
311   // Walk automatically to the next tile
312   if(direction != D_NONE)
313   {
314     Vector next_tile;
315     if (worldmap->path_ok(direction, tile_pos, &next_tile))
316     {
317       tile_pos = next_tile;
318     }
319     else
320     {
321       msg_warning << "Tilemap data is buggy" << std::endl;
322       stop();
323     }
324   }
325 }
326
327 void
328 Tux::updateInputDirection()
329 {
330   if(main_controller->hold(Controller::UP)) input_direction = D_NORTH;
331   else if(main_controller->hold(Controller::DOWN)) input_direction = D_SOUTH;
332   else if(main_controller->hold(Controller::LEFT)) input_direction = D_WEST;
333   else if(main_controller->hold(Controller::RIGHT)) input_direction = D_EAST;
334 }
335
336
337 void
338 Tux::update(float elapsed_time)
339 {
340   updateInputDirection(); 
341   if (moving) tryContinueWalking(elapsed_time); else tryStartWalking();
342 }
343
344 //---------------------------------------------------------------------------
345
346 WorldMap::WorldMap()
347   : tux(0), solids(0)
348 {
349   tile_manager = new TileManager("images/worldmap.strf");
350   
351   tux = new Tux(this);
352   add_object(tux);
353     
354   messagedot = new Surface("images/worldmap/common/messagedot.png");
355   teleporterdot = sprite_manager->create("images/worldmap/common/teleporter.sprite");
356
357   name = "<no title>";
358   music = "music/salcon.ogg";
359   intro_displayed = false;
360
361   total_stats.reset();
362 }
363
364 WorldMap::~WorldMap()
365 {
366   clear_objects();
367   for(SpawnPoints::iterator i = spawn_points.begin();
368       i != spawn_points.end(); ++i) {
369     delete *i;
370   }
371   for(Levels::iterator i = levels.begin(); i != levels.end(); ++i) {
372     Level& level = *i;
373     delete level.sprite;
374   }
375   for(SpecialTiles::iterator i = special_tiles.begin(); i != special_tiles.end(); ++i) {
376     delete i->sprite;
377   }
378   
379   delete tile_manager;
380
381   delete messagedot;
382   delete teleporterdot;
383 }
384
385 void
386 WorldMap::add_object(GameObject* object)
387 {
388   TileMap* tilemap = dynamic_cast<TileMap*> (object);
389   if(tilemap != 0 && tilemap->is_solid()) {
390     solids = tilemap;
391   }
392
393   game_objects.push_back(object);
394 }
395
396 void
397 WorldMap::clear_objects()
398 {
399   for(GameObjects::iterator i = game_objects.begin();
400       i != game_objects.end(); ++i)
401     delete *i;
402   game_objects.clear();
403   solids = 0;
404   tux = new Tux(this);
405   add_object(tux);
406 }
407
408 // Don't forget to set map_filename before calling this
409 void
410 WorldMap::load_map()
411 {
412   levels_path = FileSystem::dirname(map_filename);
413
414   try {
415     lisp::Parser parser;
416     std::auto_ptr<lisp::Lisp> root (parser.parse(map_filename));
417
418     const lisp::Lisp* lisp = root->get_lisp("supertux-level");
419     if(!lisp)
420       throw std::runtime_error("file isn't a supertux-level file.");
421
422     lisp->get("name", name);
423     
424     const lisp::Lisp* sector = lisp->get_lisp("sector");
425     if(!sector)
426       throw std::runtime_error("No sector sepcified in worldmap file.");
427     
428     clear_objects();
429     lisp::ListIterator iter(sector);
430     while(iter.next()) {
431       if(iter.item() == "tilemap") {
432         add_object(new TileMap(*(iter.lisp()), tile_manager));
433       } else if(iter.item() == "background") {
434         add_object(new Background(*(iter.lisp())));
435       } else if(iter.item() == "music") {
436         iter.value()->get(music);
437       } else if(iter.item() == "intro-script") {
438         iter.value()->get(intro_script);
439       } else if(iter.item() == "worldmap-spawnpoint") {
440         SpawnPoint* sp = new SpawnPoint(iter.lisp());
441         spawn_points.push_back(sp);
442       } else if(iter.item() == "level") {
443         parse_level_tile(iter.lisp());
444       } else if(iter.item() == "special-tile") {
445         parse_special_tile(iter.lisp());
446       } else if(iter.item() == "name") {
447         // skip
448       } else {
449         msg_warning << "Unknown token '" << iter.item() << "' in worldmap" << std::endl;
450       }
451     }
452     if(solids == 0)
453       throw std::runtime_error("No solid tilemap specified");
454
455     // search for main spawnpoint
456     for(SpawnPoints::iterator i = spawn_points.begin();
457         i != spawn_points.end(); ++i) {
458       SpawnPoint* sp = *i;
459       if(sp->name == "main") {
460         Vector p = sp->pos;
461         tux->set_tile_pos(p);
462         break;
463       }
464     }
465
466   } catch(std::exception& e) {
467     std::stringstream msg;
468     msg << "Problem when parsing worldmap '" << map_filename << "': " <<
469       e.what();
470     throw std::runtime_error(msg.str());
471   }
472 }
473
474 void
475 WorldMap::parse_special_tile(const lisp::Lisp* lisp)
476 {
477   SpecialTile special_tile;
478   
479   lisp->get("x", special_tile.pos.x);
480   lisp->get("y", special_tile.pos.y);
481
482   std::string sprite;
483   if (lisp->get("sprite", sprite)) {
484     special_tile.sprite = sprite_manager->create(sprite);
485   } else {
486     special_tile.sprite = 0;
487   }
488
489   lisp->get("map-message", special_tile.map_message);
490   special_tile.passive_message = false;
491   lisp->get("passive-message", special_tile.passive_message);
492   special_tile.teleport_dest = Vector(-1,-1);
493   lisp->get("teleport-to-x", special_tile.teleport_dest.x);
494   lisp->get("teleport-to-y", special_tile.teleport_dest.y);
495   special_tile.invisible = false;
496   lisp->get("invisible-tile", special_tile.invisible);
497
498   special_tile.apply_action_north = true;
499   special_tile.apply_action_south = true;
500   special_tile.apply_action_east = true;
501   special_tile.apply_action_west = true;
502
503   std::string apply_direction;
504   lisp->get("apply-to-direction", apply_direction);
505   if(!apply_direction.empty()) {
506     special_tile.apply_action_north = false;
507     special_tile.apply_action_south = false;
508     special_tile.apply_action_east = false;
509     special_tile.apply_action_west = false;
510     if(apply_direction.find("north") != std::string::npos)
511       special_tile.apply_action_north = true;
512     if(apply_direction.find("south") != std::string::npos)
513       special_tile.apply_action_south = true;
514     if(apply_direction.find("east") != std::string::npos)
515       special_tile.apply_action_east = true;
516     if(apply_direction.find("west") != std::string::npos)
517       special_tile.apply_action_west = true;
518   }
519   
520   special_tiles.push_back(special_tile);
521 }
522
523 void
524 WorldMap::parse_level_tile(const lisp::Lisp* level_lisp)
525 {
526   Level level;
527
528   level.solved = false;
529                   
530   level.north = true;
531   level.east  = true;
532   level.south = true;
533   level.west  = true;
534
535   std::string sprite = "images/worldmap/common/leveldot.sprite";
536   level_lisp->get("sprite", sprite);
537   level.sprite = sprite_manager->create(sprite);
538
539   level_lisp->get("extro-script", level.extro_script);
540   level_lisp->get("next-worldmap", level.next_worldmap);
541
542   level.quit_worldmap = false;
543   level_lisp->get("quit-worldmap", level.quit_worldmap);
544
545   level_lisp->get("name", level.name);
546   
547   if (!PHYSFS_exists((levels_path + level.name).c_str()))
548   {
549         // Do we want to bail out instead...? We might get messages from modders
550         // who can't make their levels run because they're too dumb to watch
551         // their terminals...
552     msg_warning << "level file '" << level.name << "' does not exist and will not be added to the worldmap" << std::endl;
553     return;
554   }
555
556   level_lisp->get("x", level.pos.x);
557   level_lisp->get("y", level.pos.y);
558
559   level.auto_path = true;
560   level_lisp->get("auto-path", level.auto_path);
561
562   level.vertical_flip = false;
563   level_lisp->get("vertical-flip", level.vertical_flip);
564
565   levels.push_back(level);
566 }
567
568 void
569 WorldMap::get_level_title(Level& level)
570 {
571   /** get special_tile's title */
572   level.title = "<no title>";
573
574   try {
575     lisp::Parser parser;
576     std::auto_ptr<lisp::Lisp> root (parser.parse(levels_path + level.name));
577
578     const lisp::Lisp* level_lisp = root->get_lisp("supertux-level");
579     if(!level_lisp)
580       return;
581     
582     level_lisp->get("name", level.title);
583   } catch(std::exception& e) {
584     msg_warning << "Problem when reading leveltitle: " << e.what() << std::endl;
585     return;
586   }
587 }
588
589 void WorldMap::calculate_total_stats()
590 {
591   total_stats.reset();
592   for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
593     {
594     if (i->solved)
595       {
596       total_stats += i->statistics;
597       }
598     }
599 }
600
601 void
602 WorldMap::on_escape_press()
603 {
604   // Show or hide the menu
605   if(!Menu::current()) {
606     Menu::set_current(worldmap_menu);
607     tux->set_direction(D_NONE);  // stop tux movement when menu is called
608   } else {
609     Menu::set_current(0);
610   }
611 }
612
613 Vector
614 WorldMap::get_next_tile(Vector pos, Direction direction)
615 {
616   switch(direction) {
617     case D_WEST:
618       pos.x -= 1;
619       break;
620     case D_EAST:
621       pos.x += 1;
622       break;
623     case D_NORTH:
624       pos.y -= 1;
625       break;
626     case D_SOUTH:
627       pos.y += 1;
628       break;
629     case D_NONE:
630       break;
631   }
632   return pos;
633 }
634
635 bool
636 WorldMap::path_ok(Direction direction, Vector old_pos, Vector* new_pos)
637 {
638   *new_pos = get_next_tile(old_pos, direction);
639
640   if (!(new_pos->x >= 0 && new_pos->x < solids->get_width()
641         && new_pos->y >= 0 && new_pos->y < solids->get_height()))
642     { // New position is outsite the tilemap
643       return false;
644     }
645   else
646     { // Check if the tile allows us to go to new_pos
647       switch(direction)
648         {
649         case D_WEST:
650           return (at(old_pos)->getData() & Tile::WORLDMAP_WEST
651               && at(*new_pos)->getData() & Tile::WORLDMAP_EAST);
652
653         case D_EAST:
654           return (at(old_pos)->getData() & Tile::WORLDMAP_EAST
655               && at(*new_pos)->getData() & Tile::WORLDMAP_WEST);
656
657         case D_NORTH:
658           return (at(old_pos)->getData() & Tile::WORLDMAP_NORTH
659               && at(*new_pos)->getData() & Tile::WORLDMAP_SOUTH);
660
661         case D_SOUTH:
662           return (at(old_pos)->getData() & Tile::WORLDMAP_SOUTH
663               && at(*new_pos)->getData() & Tile::WORLDMAP_NORTH);
664
665         case D_NONE:
666           assert(!"path_ok() can't work if direction is NONE");
667         }
668       return false;
669     }
670 }
671
672 void
673 WorldMap::finished_level(const std::string& filename)
674 {
675   // TODO calculate level from filename?
676   (void) filename;
677   Level* level = at_level();
678
679   bool old_level_state = level->solved;
680   level->solved = true;
681   level->sprite->set_action("solved");
682
683   // deal with statistics
684   level->statistics.merge(global_stats);
685   calculate_total_stats();
686
687   save_state();
688
689   if (old_level_state != level->solved && level->auto_path) {
690     // Try to detect the next direction to which we should walk
691     // FIXME: Mostly a hack
692     Direction dir = D_NONE;
693   
694     const Tile* tile = at(tux->get_tile_pos());
695
696     if (tile->getData() & Tile::WORLDMAP_NORTH
697         && tux->back_direction != D_NORTH)
698       dir = D_NORTH;
699     else if (tile->getData() & Tile::WORLDMAP_SOUTH
700         && tux->back_direction != D_SOUTH)
701       dir = D_SOUTH;
702     else if (tile->getData() & Tile::WORLDMAP_EAST
703         && tux->back_direction != D_EAST)
704       dir = D_EAST;
705     else if (tile->getData() & Tile::WORLDMAP_WEST
706         && tux->back_direction != D_WEST)
707       dir = D_WEST;
708
709     if (dir != D_NONE) {
710       tux->set_direction(dir);
711     }
712   }
713
714   if (level->extro_script != "") {
715     /* TODO
716     try {
717       std::auto_ptr<ScriptInterpreter> interpreter
718         (new ScriptInterpreter(levels_path));
719       std::istringstream in(level->extro_script);
720       interpreter->run_script(in, "level-extro-script");
721       add_object(interpreter.release());
722     } catch(std::exception& e) {
723       msg_fatal << "Couldn't run level-extro-script:" << e.what() << std::endl;
724     }
725     */
726   }
727   
728   if (!level->next_worldmap.empty()) {
729     // Load given worldmap
730     loadmap(level->next_worldmap);
731   }
732   
733   if (level->quit_worldmap)
734     main_loop->exit_screen();
735 }
736
737 void
738 WorldMap::update(float delta)
739 {
740   Menu* menu = Menu::current();
741   if(menu) {
742     menu->update();
743
744     if(menu == worldmap_menu) {
745       switch (worldmap_menu->check())
746       {
747         case MNID_RETURNWORLDMAP: // Return to game  
748           Menu::set_current(0);
749           break;
750         case MNID_QUITWORLDMAP: // Quit Worldmap
751           main_loop->exit_screen();
752           break;
753       }
754     }
755
756     return;
757   }
758
759   // update GameObjects
760   for(GameObjects::iterator i = game_objects.begin();
761       i != game_objects.end(); ++i) {
762     GameObject* object = *i;
763     object->update(delta);
764   }
765
766   // remove old GameObjects
767   for(GameObjects::iterator i = game_objects.begin();
768       i != game_objects.end(); ) {
769     GameObject* object = *i;
770     if(!object->is_valid()) {
771       delete object;
772       i = game_objects.erase(i);
773     } else {
774       ++i;
775     }
776   }
777
778   // position "camera"
779   Vector tux_pos = tux->get_pos();
780   camera_offset.x = tux_pos.x - SCREEN_WIDTH/2;
781   camera_offset.y = tux_pos.y - SCREEN_HEIGHT/2;
782
783   if (camera_offset.x < 0)
784     camera_offset.x = 0;
785   if (camera_offset.y < 0)
786     camera_offset.y = 0;
787
788   if (camera_offset.x > solids->get_width()*32 - SCREEN_WIDTH)
789     camera_offset.x = solids->get_width()*32 - SCREEN_WIDTH;
790   if (camera_offset.y > solids->get_height()*32 - SCREEN_HEIGHT)
791     camera_offset.y = solids->get_height()*32 - SCREEN_HEIGHT;
792
793   // handle input
794   bool enter_level = false;
795   if(main_controller->pressed(Controller::ACTION)
796       || main_controller->pressed(Controller::JUMP)
797       || main_controller->pressed(Controller::MENU_SELECT))
798     enter_level = true;
799   if(main_controller->pressed(Controller::PAUSE_MENU))
800     on_escape_press();
801   
802   if (enter_level && !tux->is_moving())
803     {
804       /* Check special tile action */
805       SpecialTile* special_tile = at_special_tile();
806       if(special_tile)
807         {
808         if (special_tile->teleport_dest != Vector(-1,-1))
809           {
810           // TODO: an animation, camera scrolling or a fading would be a nice touch
811           sound_manager->play("sounds/warp.wav");
812           tux->back_direction = D_NONE;
813           tux->set_tile_pos(special_tile->teleport_dest);
814           SDL_Delay(1000);
815           }
816         }
817
818       /* Check level action */
819       Level* level = at_level();
820       if (!level) {
821         msg_warning << "No level to enter at: " << tux->get_tile_pos().x << ", " << tux->get_tile_pos().y << std::endl;
822         return;
823       }
824
825       if (level->pos == tux->get_tile_pos()) {
826         // do a shriking fade to the level
827         shrink_fade(Vector((level->pos.x*32 + 16 + offset.x),
828                            (level->pos.y*32 + 16 + offset.y)), 500);
829
830         try {
831           GameSession *session =
832             new GameSession(levels_path + level->name,
833                 ST_GL_LOAD_LEVEL_FILE, &level->statistics);
834           main_loop->push_screen(session);
835         } catch(std::exception& e) {
836           msg_fatal << "Couldn't load level: " << e.what() << std::endl;
837         }
838       }
839     }
840   else
841     {
842 //      tux->set_direction(input_direction);
843     }
844 }
845
846 const Tile*
847 WorldMap::at(Vector p)
848 {
849   return solids->get_tile((int) p.x, (int) p.y);
850 }
851
852 WorldMap::Level*
853 WorldMap::at_level()
854 {
855   for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
856     {
857       if (i->pos == tux->get_tile_pos())
858         return &*i; 
859     }
860
861   return 0;
862 }
863
864 WorldMap::SpecialTile*
865 WorldMap::at_special_tile()
866 {
867   for(SpecialTiles::iterator i = special_tiles.begin(); i != special_tiles.end(); ++i)
868     {
869       if (i->pos == tux->get_tile_pos())
870         return &*i; 
871     }
872
873   return 0;
874 }
875
876 void
877 WorldMap::draw(DrawingContext& context)
878 {
879   context.push_transform();
880   context.set_translation(camera_offset);
881   
882   for(GameObjects::iterator i = game_objects.begin();
883       i != game_objects.end(); ++i) {
884     GameObject* object = *i;
885     object->draw(context);
886   }
887   
888   for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
889     {
890       const Level& level = *i;
891       level.sprite->draw(context, level.pos*32 + Vector(16, 16), LAYER_TILES+1);
892     }
893
894   for(SpecialTiles::iterator i = special_tiles.begin(); i != special_tiles.end(); ++i)
895     {
896       if(i->invisible)
897         continue;
898
899       if (i->sprite)
900         i->sprite->draw(context, i->pos*32 + Vector(16, 16), LAYER_TILES+1);
901
902       else if (i->teleport_dest != Vector(-1, -1))
903         teleporterdot->draw(context, i->pos*32 + Vector(16, 16), LAYER_TILES+1);
904
905       else if (!i->map_message.empty() && !i->passive_message)
906         context.draw_surface(messagedot,
907                 Vector(i->pos.x*32, i->pos.y*32), LAYER_TILES+1);
908     }
909
910   draw_status(context);
911   context.pop_transform();
912 }
913
914 void
915 WorldMap::draw_status(DrawingContext& context)
916 {
917   context.push_transform();
918   context.set_translation(Vector(0, 0));
919  
920   player_status->draw(context);
921
922   if (!tux->is_moving())
923     {
924       for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
925         {
926           if (i->pos == tux->get_tile_pos())
927             {
928               if(i->title == "")
929                 get_level_title(*i);
930
931               context.draw_text(white_text, i->title, 
932                   Vector(SCREEN_WIDTH/2,
933                          SCREEN_HEIGHT - white_text->get_height() - 30),
934                   CENTER_ALLIGN, LAYER_FOREGROUND1);
935
936               i->statistics.draw_worldmap_info(context);
937               break;
938             }
939         }
940       for(SpecialTiles::iterator i = special_tiles.begin(); i != special_tiles.end(); ++i)
941         {
942           if (i->pos == tux->get_tile_pos())
943             {
944                /* Display an in-map message in the map, if any as been selected */
945               if(!i->map_message.empty() && !i->passive_message)
946                 context.draw_text(gold_text, i->map_message, 
947                     Vector(SCREEN_WIDTH/2,
948                            SCREEN_HEIGHT - white_text->get_height() - 60),
949                     CENTER_ALLIGN, LAYER_FOREGROUND1);
950               break;
951             }
952         }
953     }
954   /* Display a passive message in the map, if needed */
955   if(passive_message_timer.started())
956     context.draw_text(gold_text, passive_message, 
957             Vector(SCREEN_WIDTH/2, SCREEN_HEIGHT - white_text->get_height() - 60),
958             CENTER_ALLIGN, LAYER_FOREGROUND1);
959
960   context.pop_transform();
961 }
962
963 void
964 WorldMap::setup()
965 {
966   sound_manager->play_music(music);
967   Menu::set_current(NULL);
968
969   current_ = this;
970   load_state();
971 }
972
973 static void store_float(HSQUIRRELVM vm, const char* name, float val)
974 {
975   sq_pushstring(vm, name, -1);
976   sq_pushfloat(vm, val);
977   if(SQ_FAILED(sq_createslot(vm, -3)))
978     throw Scripting::SquirrelError(vm, "Couldn't add float value to table");
979 }
980
981 /*
982 static void store_int(HSQUIRRELVM vm, const char* name, int val)
983 {
984   sq_pushstring(vm, name, -1);
985   sq_pushinteger(vm, val);
986   if(SQ_FAILED(sq_createslot(vm, -3)))
987     throw Scripting::SquirrelError(vm, "Couldn't add float value to table");
988 }
989 */
990
991 static void store_string(HSQUIRRELVM vm, const char* name, const std::string& val)
992 {
993   sq_pushstring(vm, name, -1);
994   sq_pushstring(vm, val.c_str(), val.length());
995   if(SQ_FAILED(sq_createslot(vm, -3)))
996     throw Scripting::SquirrelError(vm, "Couldn't add float value to table");
997 }
998
999 static void store_bool(HSQUIRRELVM vm, const char* name, bool val)
1000 {
1001   sq_pushstring(vm, name, -1);
1002   sq_pushbool(vm, val ? SQTrue : SQFalse);
1003   if(SQ_FAILED(sq_createslot(vm, -3)))
1004     throw Scripting::SquirrelError(vm, "Couldn't add float value to table");
1005 }
1006
1007 static float read_float(HSQUIRRELVM vm, const char* name)
1008 {
1009   sq_pushstring(vm, name, -1);
1010   if(SQ_FAILED(sq_get(vm, -2))) {
1011     std::ostringstream msg;
1012     msg << "Couldn't get float value for '" << name << "' from table";
1013     throw Scripting::SquirrelError(vm, msg.str());
1014   }
1015   
1016   float result;
1017   if(SQ_FAILED(sq_getfloat(vm, -1, &result))) {
1018     std::ostringstream msg;
1019     msg << "Couldn't get float value for '" << name << "' from table";
1020     throw Scripting::SquirrelError(vm, msg.str());
1021   }
1022   sq_pop(vm, 1);
1023
1024   return result;
1025 }
1026
1027 static std::string read_string(HSQUIRRELVM vm, const char* name)
1028 {
1029   sq_pushstring(vm, name, -1);
1030   if(SQ_FAILED(sq_get(vm, -2))) {
1031     std::ostringstream msg;
1032     msg << "Couldn't get string value for '" << name << "' from table";
1033     throw Scripting::SquirrelError(vm, msg.str());
1034   }
1035   
1036   const char* result;
1037   if(SQ_FAILED(sq_getstring(vm, -1, &result))) {
1038     std::ostringstream msg;
1039     msg << "Couldn't get string value for '" << name << "' from table";
1040     throw Scripting::SquirrelError(vm, msg.str());
1041   }
1042   sq_pop(vm, 1);
1043
1044   return std::string(result);
1045 }
1046
1047 static bool read_bool(HSQUIRRELVM vm, const char* name)
1048 {
1049   sq_pushstring(vm, name, -1);
1050   if(SQ_FAILED(sq_get(vm, -2))) {
1051     std::ostringstream msg;
1052     msg << "Couldn't get bool value for '" << name << "' from table";
1053     throw Scripting::SquirrelError(vm, msg.str());
1054   } 
1055   
1056   SQBool result;
1057   if(SQ_FAILED(sq_getbool(vm, -1, &result))) {
1058     std::ostringstream msg;
1059     msg << "Couldn't get bool value for '" << name << "' from table";
1060     throw Scripting::SquirrelError(vm, msg.str());
1061   }
1062   sq_pop(vm, 1);
1063
1064   return result == SQTrue;
1065 }
1066
1067 void
1068 WorldMap::save_state()
1069 {
1070   HSQUIRRELVM vm = ScriptManager::instance->get_vm();
1071   int oldtop = sq_gettop(vm);
1072
1073   try {
1074     // get state table
1075     sq_pushroottable(vm);
1076     sq_pushstring(vm, "state", -1);
1077     if(SQ_FAILED(sq_get(vm, -2)))
1078       throw Scripting::SquirrelError(vm, "Couldn't get state table");
1079
1080     // get or create worlds table
1081     sq_pushstring(vm, "worlds", -1);
1082     if(SQ_FAILED(sq_get(vm, -2))) {
1083       sq_pushstring(vm, "worlds", -1);
1084       sq_newtable(vm);
1085       if(SQ_FAILED(sq_createslot(vm, -3)))
1086         throw Scripting::SquirrelError(vm, "Couldn't create state.worlds");
1087
1088       sq_pushstring(vm, "worlds", -1);
1089       if(SQ_FAILED(sq_get(vm, -2)))
1090         throw Scripting::SquirrelError(vm, "Couldn't create.get state.worlds");
1091     }
1092     
1093     sq_pushstring(vm, map_filename.c_str(), map_filename.length());
1094     if(SQ_FAILED(sq_deleteslot(vm, -2, SQFalse)))
1095       sq_pop(vm, 1);
1096
1097     // construct new table for this worldmap
1098     sq_pushstring(vm, map_filename.c_str(), map_filename.length());
1099     sq_newtable(vm);
1100
1101     // store tux
1102     sq_pushstring(vm, "tux", -1);
1103     sq_newtable(vm);
1104     
1105     store_float(vm, "x", tux->get_tile_pos().x);
1106     store_float(vm, "y", tux->get_tile_pos().y);
1107     store_string(vm, "back", direction_to_string(tux->back_direction));
1108
1109     sq_createslot(vm, -3);
1110     
1111     // levels...
1112     sq_pushstring(vm, "levels", -1);
1113     sq_newtable(vm);
1114
1115     for(Levels::iterator i = levels.begin(); i != levels.end(); ++i) {
1116       if (i->solved) {
1117         sq_pushstring(vm, i->name.c_str(), -1);
1118         sq_newtable(vm);
1119
1120         store_bool(vm, "solved", true);            
1121         // TODO write statistics
1122         // i->statistics.write(writer);
1123
1124         sq_createslot(vm, -3);
1125       }
1126     }
1127     
1128     sq_createslot(vm, -3);
1129
1130     // push world into worlds table
1131     sq_createslot(vm, -3);
1132   } catch(std::exception& e) {
1133     sq_settop(vm, oldtop);
1134   }
1135
1136   sq_settop(vm, oldtop);
1137 }
1138
1139 void
1140 WorldMap::load_state()
1141 {
1142   HSQUIRRELVM vm = ScriptManager::instance->get_vm();
1143   int oldtop = sq_gettop(vm);
1144  
1145   try {
1146     // get state table
1147     sq_pushroottable(vm);
1148     sq_pushstring(vm, "state", -1);
1149     if(SQ_FAILED(sq_get(vm, -2)))
1150       throw Scripting::SquirrelError(vm, "Couldn't get state table");
1151
1152     // get worlds table
1153     sq_pushstring(vm, "worlds", -1);
1154     if(SQ_FAILED(sq_get(vm, -2)))
1155       throw Scripting::SquirrelError(vm, "Couldn't get state.worlds");
1156
1157     // get table for our world
1158     sq_pushstring(vm, map_filename.c_str(), map_filename.length());
1159     if(SQ_FAILED(sq_get(vm, -2)))
1160       throw Scripting::SquirrelError(vm, "Couldn't get state.world.mapfilename");
1161
1162     // load tux
1163     sq_pushstring(vm, "tux", -1);
1164     if(SQ_FAILED(sq_get(vm, -2)))
1165       throw Scripting::SquirrelError(vm, "Couldn't get tux");
1166
1167     Vector p;
1168     p.x = read_float(vm, "x");
1169     p.y = read_float(vm, "y");
1170     std::string back_str = read_string(vm, "back");
1171     tux->back_direction = string_to_direction(back_str);
1172     tux->set_tile_pos(p);
1173
1174     sq_pop(vm, 1);
1175
1176     // load levels
1177     sq_pushstring(vm, "levels", -1);
1178     if(SQ_FAILED(sq_get(vm, -2)))
1179       throw Scripting::SquirrelError(vm, "Couldn't get levels");
1180
1181     for(Levels::iterator i = levels.begin(); i != levels.end(); ++i) {
1182       sq_pushstring(vm, i->name.c_str(), -1);
1183       if(SQ_SUCCEEDED(sq_get(vm, -2))) {
1184         i->solved = read_bool(vm, "solved");
1185         i->sprite->set_action(i->solved ? "solved" : "default");
1186         // i->statistics.parse(*level);
1187         sq_pop(vm, 1);
1188       }
1189     }
1190     sq_pop(vm, 1);
1191
1192   } catch(std::exception& e) {
1193     msg_debug << "Not loading worldmap state: " << e.what() << std::endl;
1194   }
1195   sq_settop(vm, oldtop);
1196 }
1197     
1198 void
1199 WorldMap::loadmap(const std::string& filename)
1200 {
1201   savegame_file = "";
1202   map_filename = filename;
1203   load_map();
1204 }
1205
1206 } // namespace WorldMapNS