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