Auto-run levels are automatically marked as solved.
[supertux.git] / src / worldmap / worldmap.cpp
index e508596..c74e945 100644 (file)
@@ -39,7 +39,7 @@
 #include "object/background.hpp"
 #include "object/decal.hpp"
 #include "object/tilemap.hpp"
-#include "physfs/ifile_stream.hpp"
+#include "physfs/ifile_streambuf.hpp"
 #include "scripting/squirrel_error.hpp"
 #include "scripting/squirrel_util.hpp"
 #include "sprite/sprite.hpp"
@@ -80,7 +80,7 @@ WorldMap* WorldMap::current_ = NULL;
 WorldMap::WorldMap(const std::string& filename, PlayerStatus* player_status, const std::string& force_spawnpoint) :
   tux(0),
   player_status(player_status),
-  tileset(NULL), 
+  tileset(NULL),
   free_tileset(false),
   worldmap_menu(),
   camera_offset(),
@@ -101,9 +101,9 @@ WorldMap::WorldMap(const std::string& filename, PlayerStatus* player_status, con
   total_stats(),
   worldmap_table(),
   scripts(),
-  ambient_light( 1.0f, 1.0f, 1.0f, 1.0f ), 
+  ambient_light( 1.0f, 1.0f, 1.0f, 1.0f ),
   force_spawnpoint(force_spawnpoint),
-  in_level(false), 
+  in_level(false),
   pan_pos(),
   panning(false)
 {
@@ -134,7 +134,7 @@ WorldMap::WorldMap(const std::string& filename, PlayerStatus* player_status, con
   sq_pop(global_vm, 1);
 
   sound_manager->preload("sounds/warp.wav");
-  
+
   // load worldmap objects
   load(filename);
 }
@@ -364,6 +364,32 @@ WorldMap::get_level_title(LevelTile& level)
   }
 }
 
+void
+WorldMap::get_level_target_time(LevelTile& level)
+{
+  if(last_position == tux->get_tile_pos()) {
+    level.target_time = last_target_time;
+    return;
+  }
+
+  try {
+    lisp::Parser parser;
+    const lisp::Lisp* root = parser.parse(levels_path + level.get_name());
+
+    const lisp::Lisp* level_lisp = root->get_lisp("supertux-level");
+    if(!level_lisp)
+      return;
+
+    level_lisp->get("target-time", level.target_time);
+
+    last_position = level.pos;
+    last_target_time = level.target_time;
+  } catch(std::exception& e) {
+    log_warning << "Problem when reading level target time: " << e.what() << std::endl;
+    return;
+  }
+}
+
 void WorldMap::calculate_total_stats()
 {
   total_stats.zero();
@@ -461,6 +487,12 @@ WorldMap::finished_level(Level* gamelevel)
   // deal with statistics
   level->statistics.merge(gamelevel->stats);
   calculate_total_stats();
+  get_level_target_time(*level);
+  if(level->statistics.completed(level->statistics, level->target_time)) {
+    level->perfect = true;
+    if(level->sprite->has_action("perfect"))
+      level->sprite->set_action("perfect");
+  }
 
   save_state();
 
@@ -615,15 +647,16 @@ WorldMap::update(float delta)
     }
 
     // handle input
+    Controller *controller = g_jk_controller->get_main_controller();
     bool enter_level = false;
-    if(g_main_controller->pressed(Controller::ACTION)
-       || g_main_controller->pressed(Controller::JUMP)
-       || g_main_controller->pressed(Controller::MENU_SELECT)) {
+    if(controller->pressed(Controller::ACTION)
+       || controller->pressed(Controller::JUMP)
+       || controller->pressed(Controller::MENU_SELECT)) {
       /* some people define UP and JUMP on the same key... */
-      if(!g_main_controller->pressed(Controller::UP))
+      if(!controller->pressed(Controller::UP))
         enter_level = true;
     }
-    if(g_main_controller->pressed(Controller::PAUSE_MENU))
+    if(controller->pressed(Controller::PAUSE_MENU))
       on_escape_press();
 
     // check for teleporters
@@ -644,6 +677,8 @@ WorldMap::update(float delta)
     LevelTile* level = at_level();
     if (level && (level->auto_play) && (!level->solved) && (!tux->is_moving())) {
       enter_level = true;
+      // automatically mark these levels as solved in case player aborts
+      level->solved = true;
     }
 
     if (enter_level && !tux->is_moving())
@@ -778,7 +813,7 @@ WorldMap::draw(DrawingContext& context)
   /*
   // FIXME: make this a runtime switch similar to draw_collrects/show_collrects?
   // draw visual indication of possible walk directions
-  static int flipme = 0; 
+  static int flipme = 0;
   if (flipme++ & 0x04)
   for (int x = 0; x < get_width(); x++) {
   for (int y = 0; y < get_height(); y++) {
@@ -818,8 +853,8 @@ WorldMap::draw_status(DrawingContext& context)
 
         context.draw_text(Resources::normal_font, level->title,
                           Vector(SCREEN_WIDTH/2,
-                                 SCREEN_HEIGHT - Resources::normal_font->get_height() - 30),
-                          ALIGN_CENTER, LAYER_FOREGROUND1, WorldMap::level_title_color);
+                                 SCREEN_HEIGHT - Resources::normal_font->get_height() - 10),
+                          ALIGN_CENTER, LAYER_HUD, WorldMap::level_title_color);
 
         // if level is solved, draw level picture behind stats
         /*
@@ -834,7 +869,8 @@ WorldMap::draw_status(DrawingContext& context)
           }
         */
 
-        level->statistics.draw_worldmap_info(context);
+        get_level_target_time(*level);
+        level->statistics.draw_worldmap_info(context, level->target_time);
         break;
       }
     }
@@ -901,7 +937,8 @@ WorldMap::setup()
 
   //Run default.nut just before init script
   try {
-    IFileStream in(levels_path + "/default.nut");
+    IFileStreambuf ins(levels_path + "default.nut");
+    std::istream in(&ins);
     run_script(in, "WorldMap::default.nut");
   } catch(std::exception& ) {
     // doesn't exist or erroneous; do nothing
@@ -986,6 +1023,7 @@ WorldMap::save_state()
       sq_newtable(vm);
 
       store_bool(vm, "solved", level->solved);
+      store_bool(vm, "perfect", level->perfect);
       level->statistics.serialize_to_squirrel(vm);
 
       sq_createslot(vm, -3);
@@ -1057,7 +1095,11 @@ WorldMap::load_state()
       sq_pushstring(vm, level->get_name().c_str(), -1);
       if(SQ_SUCCEEDED(sq_get(vm, -2))) {
         level->solved = read_bool(vm, "solved");
-        level->sprite->set_action(level->solved ? "solved" : "default");
+        level->perfect = read_bool(vm, "perfect");
+        if(!level->solved)
+          level->sprite->set_action("default");
+        else
+          level->sprite->set_action((level->sprite->has_action("perfect") && level->perfect) ? "perfect" : "solved");
         level->statistics.unserialize_from_squirrel(vm);
         sq_pop(vm, 1);
       }