Auto-run levels are automatically marked as solved.
[supertux.git] / src / worldmap / worldmap.cpp
index 7f3a71d..c74e945 100644 (file)
@@ -37,8 +37,9 @@
 #include "lisp/list_iterator.hpp"
 #include "lisp/parser.hpp"
 #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"
@@ -76,9 +77,10 @@ namespace worldmap {
 
 WorldMap* WorldMap::current_ = NULL;
 
-WorldMap::WorldMap(const std::string& filename, const std::string& force_spawnpoint) :
+WorldMap::WorldMap(const std::string& filename, PlayerStatus* player_status, const std::string& force_spawnpoint) :
   tux(0),
-  tileset(NULL), 
+  player_status(player_status),
+  tileset(NULL),
   free_tileset(false),
   worldmap_menu(),
   camera_offset(),
@@ -99,9 +101,9 @@ WorldMap::WorldMap(const std::string& filename, const std::string& force_spawnpo
   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)
 {
@@ -132,7 +134,7 @@ WorldMap::WorldMap(const std::string& filename, const std::string& force_spawnpo
   sq_pop(global_vm, 1);
 
   sound_manager->preload("sounds/warp.wav");
-  
+
   // load worldmap objects
   load(filename);
 }
@@ -185,11 +187,11 @@ WorldMap::add_object(GameObject* object)
 void
 WorldMap::try_expose(GameObject* object)
 {
-  ScriptInterface* interface = dynamic_cast<ScriptInterface*> (object);
-  if(interface != NULL) {
+  ScriptInterface* object_ = dynamic_cast<ScriptInterface*> (object);
+  if(object_ != NULL) {
     HSQUIRRELVM vm = scripting::global_vm;
     sq_pushobject(vm, worldmap_table);
-    interface->expose(vm, -1);
+    object_->expose(vm, -1);
     sq_pop(vm, 1);
   }
 }
@@ -197,13 +199,13 @@ WorldMap::try_expose(GameObject* object)
 void
 WorldMap::try_unexpose(GameObject* object)
 {
-  ScriptInterface* interface = dynamic_cast<ScriptInterface*> (object);
-  if(interface != NULL) {
+  ScriptInterface* object_ = dynamic_cast<ScriptInterface*> (object);
+  if(object_ != NULL) {
     HSQUIRRELVM vm = scripting::global_vm;
     SQInteger oldtop = sq_gettop(vm);
     sq_pushobject(vm, worldmap_table);
     try {
-      interface->unexpose(vm, -1);
+      object_->unexpose(vm, -1);
     } catch(std::exception& e) {
       log_warning << "Couldn't unregister object: " << e.what() << std::endl;
     }
@@ -238,7 +240,7 @@ void
 WorldMap::change(const std::string& filename, const std::string& force_spawnpoint)
 {
   g_screen_manager->exit_screen();
-  g_screen_manager->push_screen(new WorldMap(filename, force_spawnpoint));
+  g_screen_manager->push_screen(new WorldMap(filename, player_status, force_spawnpoint));
 }
 
 void
@@ -309,6 +311,9 @@ WorldMap::load(const std::string& filename)
         Teleporter* teleporter = new Teleporter(*iter.lisp());
         teleporters.push_back(teleporter);
         add_object(teleporter);
+      } else if(iter.item() == "decal") {
+        Decal* decal = new Decal(*iter.lisp());
+        add_object(decal);
       } else if(iter.item() == "ambient-light") {
         std::vector<float> vColor;
         sector->get( "ambient-light", vColor );
@@ -359,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();
@@ -456,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();
 
@@ -610,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
@@ -639,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())
@@ -666,7 +706,7 @@ WorldMap::update(float delta)
           // update state and savegame
           save_state();
 
-          g_screen_manager->push_screen(new GameSession(levelfile, &level->statistics),
+          g_screen_manager->push_screen(new GameSession(levelfile, player_status, &level->statistics),
                                    new ShrinkFade(shrinkpos, 1.0f));
           in_level = true;
         } catch(std::exception& e) {
@@ -773,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++) {
@@ -813,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
         /*
@@ -829,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;
       }
     }
@@ -896,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
@@ -981,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);
@@ -1052,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);
       }