- Refactored worldmap a bit to reuse GameObject from the rest of the game
authorMatthias Braun <matze@braunis.de>
Thu, 12 May 2005 11:25:09 +0000 (11:25 +0000)
committerMatthias Braun <matze@braunis.de>
Thu, 12 May 2005 11:25:09 +0000 (11:25 +0000)
- Moved status drawing to PlayerStatus class
- Made Level timeout an own object, so that you can explicitely enable it in
  some levels. (So all these levels with 999 time will no have no time)
- Fixed wrong encoding for level/worldmap translations
- Added workaround to tinygettext to skip the UTF-8 BOM

SVN-Revision: 2474

37 files changed:
data/images/engine/editor/clock.png [new file with mode: 0644]
data/levels/bonus1/abednego-level4.stl
data/levels/bonus1/worldmap.stwm
data/levels/misc/menu.stl
data/levels/world1/de.po
data/levels/world1/worldmap.stwm
src/badguy/badguy.cpp
src/control/joystickkeyboardcontroller.cpp
src/control/joystickkeyboardcontroller.h
src/flip_level_transformer.cpp
src/flip_level_transformer.h
src/game_session.cpp
src/game_session.h
src/gettext.h
src/gui/menu.cpp
src/gui/mousecursor.cpp
src/level.cpp
src/level.h
src/lisp/parser.cpp
src/main.cpp
src/object/level_time.cpp [new file with mode: 0644]
src/object/level_time.h [new file with mode: 0644]
src/object/player.h
src/object/tilemap.cpp
src/object/tilemap.h
src/player_status.cpp
src/player_status.h
src/sector.cpp
src/sector.h
src/tinygettext/tinygettext.cpp
src/tinygettext/tinygettext.h
src/title.cpp
src/trigger/scripttrigger.cpp
src/video/surface.cpp
src/video/surface.h
src/worldmap.cpp
src/worldmap.h

diff --git a/data/images/engine/editor/clock.png b/data/images/engine/editor/clock.png
new file mode 100644 (file)
index 0000000..ce4d584
Binary files /dev/null and b/data/images/engine/editor/clock.png differ
index 9a03c5a..ae41af7 100644 (file)
@@ -15,7 +15,6 @@
   (bkgd_red_bottom    255)
   (bkgd_green_bottom  255)
   (bkgd_blue_bottom   255)
-  (time  60)
   (gravity  10.0)
   (particle_system "snow")
   (theme "antarctica")
@@ -76,6 +75,9 @@
   (reset-points
    )
   (objects
+    (leveltime
+      (time 60)
+    )
     (spiky  (x 2171) (y 348))
     (spiky  (x 2035) (y 312))
     (spiky  (x 1947) (y 323))
index 9f60663..250c951 100644 (file)
@@ -3,8 +3,11 @@
   (properties
     (name (_ "Bonus Island I"))
     (music "salcon.mod")
-    (start_pos_x 35)
-    (start_pos_y 2)
+  )
+  (spawnpoint
+    (name "main")
+    (x 35)
+    (y 2)
   )
   (tilemap
     (width 70)
index a7e6313..eb57c88 100644 (file)
@@ -3,7 +3,6 @@
   (version 2)
   (name   (_ "Menu Level"))
   (author "SuperTux Team")
-  (time   500)
   (sector
     (name  "main")
     (music  "theme.ogg")
index 11fa716..91a252b 100644 (file)
@@ -1,4 +1,4 @@
-# German translations for world package
+# German translations for world package
 # German messages for world.
 # Copyright (C) 2004 THE world'S COPYRIGHT HOLDER
 # This file is distributed under the same license as the world package.
@@ -13,7 +13,7 @@ msgstr ""
 "Last-Translator:  <matze@braunis.de>\n"
 "Language-Team: German <de@li.org>\n"
 "MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=ISO-8859-1\n"
+"Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
@@ -45,9 +45,9 @@ msgstr ""
 "\n"
 "#Tux und Penny sassen gemtlich beim Picknick\n"
 "#in den eisigen Ebenen der Antarktis.\n"
-"#Plzlich sprang eine dunkle Kreatur hinter\n"
+"#Plötzlich sprang eine dunkle Kreatur hinter\n"
 "#einem Felsen hervor. Tux sah einen grellen\n"
-"#Blitz, dann wurde er ohnmhtig.\n"
+"#Blitz, dann wurde er ohnmächtig.\n"
 "\n"
 "#Als er aufwachte bemerkte er, dass Penny\n"
 "#verschwunden war. Wo sie eben noch gesessen\n"
@@ -82,11 +82,11 @@ msgstr "Zu den Sternen"
 
 #: basest/levels/world1/level13.stl:5
 msgid "Above the Arctic Skies"
-msgstr "er den Wolken"
+msgstr "Über den Wolken"
 
 #: basest/levels/world1/level14.stl:5
 msgid "Entrance to the Cave"
-msgstr "Hleneingang"
+msgstr "Höhleneingang"
 
 #: basest/levels/world1/level15.stl:5
 msgid "Under the Ice"
@@ -94,7 +94,7 @@ msgstr "Unter dem Eis"
 
 #: basest/levels/world1/level16.stl:5
 msgid "Living in a Fridge"
-msgstr "Leben im Khlschrank"
+msgstr "Leben im Kühlschrank"
 
 #: basest/levels/world1/level17.stl:5
 msgid "'...or is it just me?'"
@@ -126,7 +126,7 @@ msgstr "Die Flucht"
 
 #: basest/levels/world1/level24.stl:5
 msgid "The Shattered Bridge"
-msgstr "Brckentrmmer"
+msgstr "Die zertrümmerte Brücke"
 
 #: basest/levels/world1/level25.stl:5
 msgid "Arctic Ruins"
@@ -146,7 +146,7 @@ msgstr "Die Reise Beginnt"
 
 #: basest/levels/world1/level3.stl:5
 msgid "Via Nostalgica"
-msgstr "Nostalgiestra"
+msgstr "Nostalgiestraße"
 
 #: basest/levels/world1/level4.stl:5
 msgid "Tobgle Road"
@@ -162,7 +162,7 @@ msgstr "Eisige Felder"
 
 #: basest/levels/world1/level7.stl:5
 msgid "Oh no! More Snowballs!"
-msgstr "Oh Nein! Noch mehr Schneeble!"
+msgstr "Oh Nein! Noch mehr Schneebälle!"
 
 #: basest/levels/world1/level8.stl:5
 msgid "Stone Cold"
@@ -170,7 +170,7 @@ msgstr "Steinkalt"
 
 #: basest/levels/world1/level9.stl:5
 msgid "Grumbel's Sense of Snow"
-msgstr "Grumbels Schneegespr"
+msgstr "Grumbels Gespür für Schnee"
 
 #: basest/levels/world1/extro.txt:7
 msgid ""
@@ -228,17 +228,17 @@ msgstr ""
 "#dennoch vergeblich. Mit jeder meiner\n"
 "#Festungen die du eroberst, werde ich zu\n"
 "#einer weiteren fliehen. Sei nicht dumm,\n"
-"#es we das beste jetzt aufzugeben.\n"
+"#es wäre das beste jetzt aufzugeben.\n"
 "\n"
 "#Tux verliess traurig den Saal, als etwas\n"
 "#unter seinem Fuss raschelte...\n"
 "#Ein Briefumschlag mit seinem Namen!\n"
 "#In dem Umschlag war eine grobe Karte,\n"
-"#die Festungen in verschiedenen Ldern\n"
+"#die Festungen in verschiedenen Ländern\n"
 "#zeigte. Auf der Rckseite der Karte war\n"
 "#Pennys Zeichen, das Bild einer Eisblume.\n"
 "\n"
 "#Tux nahm die Karte in die Hand und rannte\n"
-"#aus der Festung. Nein, er wrde nicht\n"
+"#aus der Festung. Nein, er würde nicht\n"
 "#einfach aufgeben. Penny verliess sich auf\n"
 "#ihn.\n"
index 67c2a95..8934bc9 100644 (file)
@@ -4,11 +4,16 @@
     (name (_ "Icyisland"))
     (intro-filename "intro.txt")
     (music "salcon.mod")
-    (start_pos_x 4)
-    (start_pos_y 5))
+  )
+  (spawnpoint
+    (name "main")
+    (x 4)
+    (y 5)
+  )
   (tilemap 
     (width  40)
     (height 30)
+    (solid #t)
     (tiles
        9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  
        9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  9  11 12 9  9  9  9  9  9  9  9  9  9  9  9  
index a312986..7dcb5c8 100644 (file)
@@ -153,7 +153,7 @@ BadGuy::collision_player(Player& player, const CollisionHit& )
     return ABORT_MOVE;
   }
   // hit from above?
-  if(player.get_movement().y > 0 && player.get_bbox().p2.y <
+  if(player.get_movement().y - get_movement().y > 0 && player.get_bbox().p2.y <
       (get_bbox().p1.y + get_bbox().p2.y) / 2) {
     // if it's not is it possible to squish us, then this will hurt
     if(!collision_squished(player))
index 09220c9..6bebf6b 100644 (file)
@@ -116,6 +116,13 @@ JoystickKeyboardController::JoystickKeyboardController()
     if(i != min_joybuttons-1)
       joy_button_map.insert(std::make_pair(i, MENU_SELECT));
   }
+
+  // some joysticks or SDL seem to produce some bogus events after being opened
+  Uint32 ticks = SDL_GetTicks();
+  while(SDL_GetTicks() - ticks < 200) {
+    SDL_Event event;
+    SDL_PollEvent(&event);
+  }
 }
 
 JoystickKeyboardController::~JoystickKeyboardController()
@@ -228,6 +235,14 @@ JoystickKeyboardController::write(lisp::Writer& writer)
 }
 
 void
+JoystickKeyboardController::reset()
+{
+  Controller::reset();
+  for(size_t i = 0; i < sizeof(last_keys); ++i)
+      last_keys[i] = 0;
+}
+
+void
 JoystickKeyboardController::process_event(const SDL_Event& event)
 {
   switch(event.type) {
index 150ddb3..6520a2b 100644 (file)
@@ -43,6 +43,7 @@ public:
   void write(lisp::Writer& writer);
   void read(const lisp::Lisp& lisp);
        bool check_cheatcode(const std::string& cheatcode);
+  void reset();
 
   Menu* get_key_options_menu();
   Menu* get_joystick_options_menu();
index 6e2b2db..22f10d4 100644 (file)
@@ -17,7 +17,6 @@
 //  along with this program; if not, write to the Free Software
 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 //  02111-1307, USA.
-
 #include <config.h>
 
 #include "flip_level_transformer.h"
@@ -25,6 +24,7 @@
 #include "badguy/badguy.h"
 #include "sector.h"
 #include "tile_manager.h"
+#include "spawn_point.h"
 
 void
 FlipLevelTransformer::transform_sector(Sector* sector)
index 317629f..79b7b02 100644 (file)
@@ -17,7 +17,6 @@
 //  along with this program; if not, write to the Free Software
 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 //  02111-1307, USA.
-
 #ifndef __FLIP_LEVEL_TRANSFORMER_H__
 #define __FLIP_LEVEL_TRANSFORMER_H__
 
index 86b38e3..1402e64 100644 (file)
@@ -109,7 +109,7 @@ GameSession::restart_level()
   global_stats.reset();
   global_stats.set_total_points(COINS_COLLECTED_STAT, level->get_total_coins());
   global_stats.set_total_points(BADGUYS_KILLED_STAT, level->get_total_badguys());
-  global_stats.set_total_points(TIME_NEEDED_STAT, level->timelimit);
+  //global_stats.set_total_points(TIME_NEEDED_STAT, level->timelimit);
 
   if(reset_sector != "") {
     currentsector = level->get_sector(reset_sector);
@@ -129,7 +129,6 @@ GameSession::restart_level()
   if(mode == ST_GL_PLAY || mode == ST_GL_LOAD_LEVEL_FILE)
     levelintro();
 
-  start_timers();
   currentsector->play_music(LEVEL_MUSIC);
 
   if(capture_file != "")
@@ -219,13 +218,6 @@ GameSession::levelintro()
   wait_for_event(1.0, 3.0);
 }
 
-/* Reset Timers */
-void
-GameSession::start_timers()
-{
-  time_left.start(level->timelimit);
-}
-
 void
 GameSession::on_escape_press()
 {
@@ -315,6 +307,8 @@ GameSession::process_events()
 void
 GameSession::try_cheats()
 {
+  if(currentsector == 0)
+    return;
   Player& tux = *currentsector->player;
   
   // Cheating words (the goal of this is really for debugging,
@@ -442,7 +436,6 @@ GameSession::draw()
 
   if(Menu::current()) {
     Menu::current()->draw(*context);
-    mouse_cursor->draw(*context);
   }
 
   context->do_drawing();
@@ -601,10 +594,6 @@ GameSession::run()
 
     //frame_rate.update();
     
-    /* Handle time: */
-    if (time_left.check() && !end_sequence)
-      currentsector->player->kill(Player::KILL);
-    
     /* Handle music: */
     if (currentsector->player->invincible_timer.started() && !end_sequence)
     {
@@ -631,14 +620,18 @@ GameSession::run()
   }
  
   // just in case
+  currentsector = 0;
   main_controller->reset();
   return exit_status;
 }
 
 void
-GameSession::finish()
+GameSession::finish(bool win)
 {
-  exit_status = ES_LEVEL_FINISHED;
+  if(win)
+    exit_status = ES_LEVEL_FINISHED;
+  else
+    exit_status = ES_LEVEL_ABORT;
 }
 
 void
@@ -696,10 +689,6 @@ GameSession::start_sequence(const std::string& sequencename)
     sound_manager->play_music(level_end_song, 0);
     currentsector->player->invincible_timer.start(7.0);
 
-    // add left time to stats
-    global_stats.set_points(TIME_NEEDED_STAT,
-        int(time_left.get_period() - time_left.get_timeleft()));
-
     if(sequencename == "fireworks") {
       currentsector->add_object(new Fireworks());
     }
@@ -714,58 +703,11 @@ GameSession::start_sequence(const std::string& sequencename)
 void
 GameSession::drawstatus(DrawingContext& context)
 {
-  char str[60];
-  
-  snprintf(str, 60, " %d", global_stats.get_points(SCORE_STAT));
-  context.draw_text(white_text, _("SCORE"), Vector(0, 0), LEFT_ALLIGN, LAYER_FOREGROUND1);
-  context.draw_text(gold_text, str, Vector(96, 0), LEFT_ALLIGN, LAYER_FOREGROUND1);
-
-  if(mode == ST_GL_TEST) {
-    context.draw_text(white_text, _("Press ESC To Return"), Vector(0,20),
-                      LEFT_ALLIGN, LAYER_FOREGROUND1);
-  }
-
-  if(time_left.get_timeleft() < 0) {
-    context.draw_text(white_text, _("TIME's UP"), Vector(SCREEN_WIDTH/2, 0),
-        CENTER_ALLIGN, LAYER_FOREGROUND1);
-  } else if (time_left.get_timeleft() > TIME_WARNING
-      || int(global_time * 2.5) % 2) {
-    sprintf(str, " %d", int(time_left.get_timeleft()));
-    context.draw_text(white_text, _("TIME"),
-        Vector(SCREEN_WIDTH/2, 0), CENTER_ALLIGN, LAYER_FOREGROUND1);
-    context.draw_text(gold_text, str,
-        Vector(SCREEN_WIDTH/2 + 4*16, 0), CENTER_ALLIGN, LAYER_FOREGROUND1);
-  }
-
-  sprintf(str, " %d", player_status.coins);
-  context.draw_text(white_text, _("COINS"),
-      Vector(SCREEN_WIDTH - white_text->get_text_width(_("COINS"))-white_text->get_text_width("   99"), 0),
-        LEFT_ALLIGN, LAYER_FOREGROUND1);
-  context.draw_text(gold_text, str,
-      Vector(SCREEN_WIDTH - gold_text->get_text_width(" 99"), 0),LEFT_ALLIGN, LAYER_FOREGROUND1);
-
-  if (player_status.lives >= 5)
-    {
-      sprintf(str, "%dx", player_status.lives);
-      float x = SCREEN_WIDTH - gold_text->get_text_width(str) - tux_life->w;
-      context.draw_text(gold_text, str, Vector(x, 20), LEFT_ALLIGN, LAYER_FOREGROUND1);
-      context.draw_surface(tux_life, Vector(SCREEN_WIDTH - 16, 20),
-          LAYER_FOREGROUND1);
-    }
-  else
-    {
-      for(int i= 0; i < player_status.lives; ++i)
-        context.draw_surface(tux_life, 
-            Vector(SCREEN_WIDTH - tux_life->w*4 +(tux_life->w*i), 20),
-            LAYER_FOREGROUND1);
-    }
-
-  context.draw_text(white_text, _("LIVES"),
-      Vector(SCREEN_WIDTH - white_text->get_text_width(_("LIVES")) - white_text->get_text_width("   99"), 20),
-      LEFT_ALLIGN, LAYER_FOREGROUND1);
+  player_status.draw(context);
 
   if(config->show_fps) {
-    sprintf(str, "%2.1f", fps_fps);
+    char str[60];
+    snprintf(str, sizeof(str), "%2.1f", fps_fps);
     context.draw_text(white_text, "FPS", 
                       Vector(SCREEN_WIDTH -
                              white_text->get_text_width("FPS     "), 40),
index 5fc2f68..eb31ed4 100644 (file)
@@ -59,7 +59,6 @@ public:
 
 public:
   DrawingContext* context;
-  Timer time_left;
 
   GameSession(const std::string& levelfile, GameSessionMode mode,
               Statistics* statistics=0);
@@ -77,8 +76,8 @@ public:
   { current_ = this; }
   static GameSession* current() { return current_; }
 
-  /// ends the level as finished
-  void finish();
+  /// ends the current level
+  void finish(bool win = true);
   void respawn(const std::string& sectorname,
       const std::string& spawnpointname);
   void set_reset_point(const std::string& sectorname,
@@ -95,7 +94,6 @@ private:
   void restart_level();
 
   void check_end_conditions();
-  void start_timers();
   void process_events();
   void capture_demo_step();
 
index 8ecae1a..b12cdd2 100644 (file)
@@ -22,7 +22,12 @@ extern TinyGetText::DictionaryManager dictionary_manager;
 
 static inline const char* _(const char* message)
 {
-  return dictionary_manager.get_dictionary().translate(message).c_str();
+  return dictionary_manager.get_dictionary().translate(message);
+}
+
+static inline std::string _(const std::string& message)
+{
+  return dictionary_manager.get_dictionary().translate(message);
 }
 
 static inline const char* N_(const char* id, const char* id2, int num)
index 87d083d..d70472d 100644 (file)
@@ -76,11 +76,10 @@ bool confirm_dialog(Surface *background, std::string text)
   while(true)
     {
       SDL_Event event;
-
-      if(event.type == SDL_QUIT)
-        throw std::runtime_error("received window close event");
-      
       while (SDL_PollEvent(&event)) {
+        if(event.type == SDL_QUIT)
+          throw std::runtime_error("received window close event");
+        main_controller->process_event(event);
         dialog->event(event);
       }
 
@@ -691,6 +690,10 @@ int Menu::get_height() const
 void
 Menu::draw(DrawingContext& context)
 {
+  if(MouseCursor::current()) {
+    MouseCursor::current()->draw(context);
+  }
+  
   int menu_height = get_height();
   int menu_width = get_width();  
 
index 5a93563..8b774f7 100644 (file)
@@ -80,6 +80,6 @@ void MouseCursor::draw(DrawingContext& context)
       cur_state = state_before_click;
   }
 
-  context.draw_surface_part(cursor, Vector(w, h*cur_state),
+  context.draw_surface_part(cursor, Vector(0, h*cur_state),
           Vector(w, h), Vector(x-mid_x, y-mid_y), LAYER_GUI+100);
 }
index b097cc4..e9f8ccd 100644 (file)
@@ -51,7 +51,7 @@
 using namespace std;
 
 Level::Level()
-  : name("noname"), author("Mr. X"), timelimit(500)
+  : name("noname"), author("Mr. X")
 {
 }
 
@@ -92,8 +92,6 @@ Level::load(const std::string& filepath)
         iter.value()->get(name);
       } else if(token == "author") {
         iter.value()->get(author);
-      } else if(token == "time") {
-        iter.value()->get(timelimit);
       } else if(token == "sector") {
         Sector* sector = new Sector;
         sector->parse(*(iter.lisp()));
@@ -116,7 +114,6 @@ Level::load_old_format(const lisp::Lisp& reader)
 {
   reader.get("name", name);
   reader.get("author", author);
-  reader.get("time", timelimit);
 
   Sector* sector = new Sector;
   sector->parse_old_format(reader);
@@ -142,7 +139,6 @@ Level::save(const std::string& filename)
 
   writer->write_string("name", name, true);
   writer->write_string("author", author);
-  writer->write_int("time", timelimit);
 
   for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i) {
     Sector* sector = *i;
index 555138a..18d1803 100644 (file)
@@ -35,7 +35,6 @@ class Level
 public:
   std::string name;
   std::string author;
-  int timelimit;
   typedef std::vector<Sector*> Sectors;
   Sectors sectors;
 
index 65f163a..cd74886 100644 (file)
@@ -38,6 +38,7 @@ Parser::Parser(bool translate)
 {
   if(translate) {
     dictionary_manager = new TinyGetText::DictionaryManager();
+    dictionary_manager->set_charset("ISO8859-1");
   }
 }
 
index 494209f..a48b3f4 100644 (file)
@@ -133,7 +133,7 @@ static void find_directories()
 static void init_tinygettext()
 {
   dictionary_manager.add_directory(datadir + "/locale");
-  dictionary_manager.set_charset("iso8859-1");
+  dictionary_manager.set_charset("ISO8859-1");
 }
 
 static void print_usage(const char* argv0)
diff --git a/src/object/level_time.cpp b/src/object/level_time.cpp
new file mode 100644 (file)
index 0000000..fc0ae13
--- /dev/null
@@ -0,0 +1,70 @@
+#include <config.h>
+
+#include "level_time.h"
+
+#include <stdexcept>
+#include "main.h"
+#include "resources.h"
+#include "sector.h"
+#include "gettext.h"
+#include "object_factory.h"
+#include "object/player.h"
+#include "video/drawing_context.h"
+#include "lisp/list_iterator.h"
+
+/** When to alert player they're low on time! */
+static const float TIME_WARNING = 20;
+
+LevelTime::LevelTime(const lisp::Lisp& reader)
+{
+    float time = -1;
+    lisp::ListIterator iter(&reader);
+    while(iter.next()) {
+        if(iter.item() == "time") {
+            iter.value()->get(time);
+            break;
+        } else {
+            std::cerr << "Unknown token '" << iter.item() 
+                      << "' in LevelTime object.\n";
+        }
+    }
+    if(time < 0)
+      throw std::runtime_error("Invalid leveltime specified");
+    time_left.start(time);
+}
+
+LevelTime::~LevelTime()
+{}
+
+void
+LevelTime::update(float )
+{
+  if(time_left.check()) {
+    Sector::current()->player->kill(Player::KILL);
+  }
+}
+
+void
+LevelTime::draw(DrawingContext& context)
+{
+  context.push_transform();
+  context.set_translation(Vector(0, 0));
+
+  char str[60];
+    
+  if(time_left.get_timeleft() < 0) {
+    context.draw_text(white_text, _("TIME's UP"), Vector(SCREEN_WIDTH/2, 0),
+        CENTER_ALLIGN, LAYER_FOREGROUND1);
+  } else if (time_left.get_timeleft() > TIME_WARNING
+      || int(global_time * 2.5) % 2) {
+    snprintf(str, sizeof(str), " %d", int(time_left.get_timeleft()));
+    context.draw_text(white_text, _("TIME"),
+        Vector(SCREEN_WIDTH/2, 0), CENTER_ALLIGN, LAYER_FOREGROUND1);
+    context.draw_text(gold_text, str, Vector(SCREEN_WIDTH/2 + 4*16, 0),
+                      CENTER_ALLIGN, LAYER_FOREGROUND1);
+  }
+
+  context.pop_transform();
+}
+
+IMPLEMENT_FACTORY(LevelTime, "leveltime");
diff --git a/src/object/level_time.h b/src/object/level_time.h
new file mode 100644 (file)
index 0000000..3c7e67e
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef __LEVELTIME_H__
+#define __LEVELTIME_H__
+
+#include "game_object.h"
+#include "timer.h"
+#include "lisp/lisp.h"
+
+class LevelTime : public GameObject
+{
+public:
+    LevelTime(const lisp::Lisp& reader);
+    virtual ~LevelTime();
+
+    void update(float elapsed_time);
+    void draw(DrawingContext& context);
+
+private:
+    Timer time_left;
+};
+
+#endif
+
index 731b443..22fb0f2 100644 (file)
@@ -39,7 +39,6 @@ static const float TUX_SAFE_TIME = 1.250;
 static const float TUX_INVINCIBLE_TIME = 10.0;
 static const float TUX_INVINCIBLE_TIME_WARNING = 2.0;
 static const float TUX_FLAPPING_TIME = 1; /* How long Tux can flap his wings to gain additional jump height */
-static const float TIME_WARNING = 20;     /* When to alert player they're low on time! */
 static const float GROWING_TIME = 1.0;
 static const int GROWING_FRAMES = 7;
 
index a8ca571..15c6125 100644 (file)
@@ -45,11 +45,13 @@ TileMap::TileMap()
     flags |= FLAG_SOLID;
 }
 
-TileMap::TileMap(const lisp::Lisp& reader)
-  : solid(false), speed(1), width(0), height(0), layer(LAYER_TILES),
+TileMap::TileMap(const lisp::Lisp& reader, TileManager* new_tile_manager)
+  : solid(false), speed(1), width(-1), height(-1), layer(LAYER_TILES),
     drawing_effect(0)
 {
-  tilemanager = tile_manager;
+  tilemanager = new_tile_manager;
+  if(tilemanager == 0)
+    tilemanager = tile_manager;
 
   std::string layer_str;
   if(reader.get("layer", layer_str)) {
@@ -72,10 +74,11 @@ TileMap::TileMap(const lisp::Lisp& reader)
   }
   if(solid)
     flags |= FLAG_SOLID;
-  
-  if(!reader.get("width", width) ||
-     !reader.get("height", height))
-    throw std::runtime_error("No width or height specified in tilemap.");
+  reader.get("width", width);
+  reader.get("height", height);
+  if(width < 0 || height < 0)
+    throw std::runtime_error("Invalid/No width/height specified in tilemap.");
 
   if(!reader.get_vector("tiles", tiles))
     throw std::runtime_error("No tiles in tilemap.");
@@ -149,9 +152,11 @@ TileMap::draw(DrawingContext& context)
   /** if we don't round here, we'll have a 1 pixel gap on screen sometimes.
    * I have no idea why */
   float start_x = roundf(context.get_translation().x);
-  if(start_x < 0) start_x = 0;
+  if(start_x < 0)
+    start_x = 0;
   float start_y = roundf(context.get_translation().y);
-  if(start_y < 0) start_y = 0;
+  if(start_y < 0)
+    start_y = 0;
   float end_x = std::min(start_x + SCREEN_WIDTH, float(width * 32));
   float end_y = std::min(start_y + SCREEN_HEIGHT, float(height * 32));
   start_x -= int(start_x) % 32;
index 2aaf3ce..d647a5e 100644 (file)
@@ -41,7 +41,7 @@ class TileMap : public GameObject, public Serializable
 {
 public:
   TileMap();
-  TileMap(const lisp::Lisp& reader);
+  TileMap(const lisp::Lisp& reader, TileManager* tile_manager = 0);
   TileMap(int layer_, bool solid_, size_t width_, size_t height_);
   virtual ~TileMap();
 
index 6fcf66c..304a076 100644 (file)
@@ -22,6 +22,9 @@
 #include "lisp/lisp.h"
 #include "player_status.h"
 #include "resources.h"
+#include "gettext.h"
+#include "video/drawing_context.h"
+#include "main.h"
 
 static const int START_LIVES = 4;
 static const int MAX_LIVES = 99;
@@ -117,3 +120,43 @@ PlayerStatus::read(const lisp::Lisp& lisp)
   lisp.get("max-score-multiplier", max_score_multiplier);
 }
 
+void
+PlayerStatus::draw(DrawingContext& context)
+{
+  context.push_transform();
+  context.set_translation(Vector(0, 0));
+
+  char str[60];
+  
+  sprintf(str, " %d", player_status.coins);
+  const char* coinstext = _("COINS");
+  context.draw_text(white_text, coinstext,
+      Vector(SCREEN_WIDTH - white_text->get_text_width(coinstext) 
+              - white_text->get_text_width("   99"), 0),
+      LEFT_ALLIGN, LAYER_FOREGROUND1);
+  context.draw_text(gold_text, str,
+      Vector(SCREEN_WIDTH - gold_text->get_text_width(" 99"), 0),
+      LEFT_ALLIGN, LAYER_FOREGROUND1);
+
+  if (player_status.lives >= 5) {
+    sprintf(str, "%dx", player_status.lives);
+    float x = SCREEN_WIDTH - gold_text->get_text_width(str) - tux_life->w;
+    context.draw_text(gold_text, str, Vector(x, 20), LEFT_ALLIGN,
+                      LAYER_FOREGROUND1);
+    context.draw_surface(tux_life, Vector(SCREEN_WIDTH - 16, 20),
+                         LAYER_FOREGROUND1);
+  } else {
+    for(int i= 0; i < player_status.lives; ++i)
+      context.draw_surface(tux_life, 
+          Vector(SCREEN_WIDTH - tux_life->w*4 +(tux_life->w*i), 20),
+          LAYER_FOREGROUND1);
+  }
+
+  const char* livestext = _("LIVES");
+  context.draw_text(white_text, livestext,
+      Vector(SCREEN_WIDTH - white_text->get_text_width(livestext) 
+                - white_text->get_text_width("   99"), 20),
+      LEFT_ALLIGN, LAYER_FOREGROUND1);
+
+  context.pop_transform();
+}
index e4fb335..c1bdc25 100644 (file)
@@ -26,6 +26,7 @@
 enum BonusType {
   NO_BONUS, GROWUP_BONUS, FIRE_BONUS, ICE_BONUS
 };
+class DrawingContext;
 
 /** 
  * This class memorizes player status between different game sessions (for
@@ -42,6 +43,8 @@ public:
   void write(lisp::Writer& writer);
   void read(const lisp::Lisp& lisp);
 
+  void draw(DrawingContext& context);
+
   int  coins;
   int  lives;
   BonusType bonus;
index cc1689b..11957a4 100644 (file)
@@ -46,6 +46,7 @@
 #include "collision_grid_iterator.h"
 #include "object_factory.h"
 #include "collision.h"
+#include "spawn_point.h"
 #include "math/rect.h"
 #include "math/aatriangle.h"
 #include "object/coin.h"
@@ -145,12 +146,7 @@ Sector::parse(const lisp::Lisp& sector)
       iter.value()->get(song_title);
       load_music();
     } else if(token == "spawnpoint") {
-      const lisp::Lisp* spawnpoint_lisp = iter.lisp();
-      
-      SpawnPoint* sp = new SpawnPoint;
-      spawnpoint_lisp->get("name", sp->name);
-      spawnpoint_lisp->get("x", sp->pos.x);
-      spawnpoint_lisp->get("y", sp->pos.y);
+      SpawnPoint* sp = new SpawnPoint(iter.lisp());
       spawnpoints.push_back(sp);
     } else if(token == "init-script") {
       iter.value()->get(init_script);
index 5477221..51a135b 100644 (file)
@@ -41,13 +41,7 @@ class TileMap;
 class Bullet;
 class CollisionGrid;
 class ScriptInterpreter;
-
-class SpawnPoint
-{
-public:
-  std::string name;
-  Vector pos;
-};
+class SpawnPoint;
 
 enum MusicType {
   LEVEL_MUSIC,
index dd73e80..9ffd17c 100644 (file)
@@ -433,6 +433,20 @@ Dictionary::translate(const std::string& msgid)
       return msgid;
     }
 }
+
+const char*
+Dictionary::translate(const char* msgid)
+{
+  Entries::iterator i = entries.find(msgid);
+  if(i == entries.end() || i->second.empty()) {
+#ifdef TRANSLATION_DBEUG
+    std::cout << "Error: Couldn't translate: " << msgid << std::endl;
+#endif                                                                     
+    return msgid;
+  }
+
+  return i->second.c_str();
+}
   
 void
 Dictionary::add_translation(const std::string& msgid, const std::string& ,
@@ -477,6 +491,13 @@ public:
   {
     state = WANT_MSGID;
     line_num = 0;
+    char c = in.get();
+    if(c == (char) 0xef) { // skip UTF-8 intro that some texteditors produce
+      in.get();
+      in.get();
+    } else {
+      in.unget();
+    }
     tokenize_po(in);
   }
 
index 207ce28..a8a5846 100644 (file)
@@ -78,6 +78,8 @@ public:
 
   /** Translate the string \a msgid. */
   std::string translate(const std::string& msgid);
+
+  const char* translate(const char* msgid);
     
   /** Add a translation from \a msgid to \a msgstr to the dictionary,
       where \a msgid is the singular form of the message, msgid2 the
index 255b38d..9afb667 100644 (file)
@@ -185,14 +185,12 @@ void check_levels_contrib_menu()
 
     // TODO: slots should be available for contrib maps
     worldmap.loadgame(user_dir + "/save/" + subset.name + "-slot1.stsg");
-
     worldmap.display();  // run the map
 
     Menu::set_current(main_menu);
     resume_demo();
   } else if (current_subset != index) {
     current_subset = index;
-    // FIXME: This shouln't be busy looping
     LevelSubset& subset = * (contrib_subsets[index]);
 
     current_contrib_subset = &subset;
@@ -433,8 +431,6 @@ void title()
         Menu::set_current(main_menu);
       }
 
-      mouse_cursor->draw(context);
-     
       context.do_drawing();
 
       //frame_rate.update();
index e5bc848..7a7c27a 100644 (file)
@@ -18,8 +18,8 @@
 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\r
 //  02111-1307, USA.\r
 //\r
-\r
 #include <config.h>\r
+\r
 #include <sstream>\r
 \r
 #include "scripttrigger.h"\r
@@ -103,4 +103,5 @@ ScriptTrigger::event(Player& , EventType type)
   }\r
 }\r
 \r
-IMPLEMENT_FACTORY(ScriptTrigger, "scripttrigger")\r
+IMPLEMENT_FACTORY(ScriptTrigger, "scripttrigger");\r
+\r
index 9987482..ec846e7 100644 (file)
@@ -137,7 +137,7 @@ static int power_of_two(int input)
 }
 
 Surface::Surface(SDL_Surface* surf, bool use_alpha)
-    : data(surf, use_alpha), w(0), h(0)
+    : impl(0), data(surf, use_alpha), w(0), h(0)
 {
   impl = data.create();
   if (impl)
@@ -149,7 +149,7 @@ Surface::Surface(SDL_Surface* surf, bool use_alpha)
 }
 
 Surface::Surface(const std::string& file, bool use_alpha)
-    : data(file, use_alpha), w(0), h(0)
+    : impl(0), data(file, use_alpha), w(0), h(0)
 {
   impl = data.create();
   if (impl)
@@ -161,7 +161,7 @@ Surface::Surface(const std::string& file, bool use_alpha)
 }
 
 Surface::Surface(const std::string& file, int x, int y, int w_, int h_, bool use_alpha)
-    : data(file, x, y, w_, h_, use_alpha), w(0), h(0)
+    : impl(0), data(file, x, y, w_, h_, use_alpha), w(0), h(0)
 {
   impl = data.create();
   if (impl)
@@ -173,7 +173,7 @@ Surface::Surface(const std::string& file, int x, int y, int w_, int h_, bool use
 }
 
 Surface::Surface(Color top_background, Color bottom_background, int w_, int h_)
-    : data(top_background, bottom_background, w_, h_), w(0), h(0)
+    : impl(0), data(top_background, bottom_background, w_, h_), w(0), h(0)
 {
   impl = data.create();
   if (impl)
@@ -296,8 +296,7 @@ sdl_surface_part_from_file(const std::string& file, int x, int y, int w, int h,
   SDL_Surface * conv;
 
   temp = IMG_Load(file.c_str());
-
-  if (temp == NULL) {
+  if (temp == 0) {
     std::stringstream msg;
     msg << "Couldn't load '" << file << "': " << SDL_GetError();
     throw std::runtime_error(msg.str());
@@ -346,8 +345,7 @@ sdl_surface_from_file(const std::string& file, bool use_alpha)
   SDL_Surface* temp;
 
   temp = IMG_Load(file.c_str());
-
-  if (temp == NULL) {
+  if (temp == 0) {
     std::stringstream msg;
     msg << "Couldn't load file '" << file << "': " << SDL_GetError();
     throw std::runtime_error(msg.str());
@@ -454,11 +452,13 @@ sdl_surface_from_gradient(Color top, Color bottom, int w, int h)
 //---------------------------------------------------------------------------
 
 SurfaceImpl::SurfaceImpl()
+  : sdl_surface(0)
 {}
 
 SurfaceImpl::~SurfaceImpl()
 {
-  SDL_FreeSurface(sdl_surface);
+  if(sdl_surface != 0)
+    SDL_FreeSurface(sdl_surface);
 }
 
 SDL_Surface* SurfaceImpl::get_sdl_surface() const
index 55f6ed0..b7adb5f 100644 (file)
@@ -95,8 +95,8 @@ public:
 class Surface
 {
 public:
-  SurfaceData data;
   SurfaceImpl* impl;
+  SurfaceData data;
   int w;
   int h;
   
index d90afe7..e193b0f 100644 (file)
 #include "player_status.h"
 #include "textscroller.h"
 #include "main.h"
+#include "spawn_point.h"
 #include "file_system.h"
 #include "gui/menu.h"
 #include "gui/mousecursor.h"
 #include "control/joystickkeyboardcontroller.h"
+#include "object/background.h"
+#include "object/tilemap.h"
 
 Menu* worldmap_menu  = 0;
 
@@ -116,8 +119,6 @@ Tux::Tux(WorldMap* worldmap_)
   
   offset = 0;
   moving = false;
-  tile_pos.x = worldmap->get_start_x();
-  tile_pos.y = worldmap->get_start_y();
   direction = D_NONE;
   input_direction = D_NONE;
 }
@@ -176,7 +177,7 @@ Tux::get_pos()
       break;
     }
   
-  return Vector((int)x, (int)y); 
+  return Vector(x, y);
 }
 
 void
@@ -343,21 +344,21 @@ Tux::update(float delta)
 //---------------------------------------------------------------------------
 
 WorldMap::WorldMap()
+  : tux(0), solids(0)
 {
   tile_manager = new TileManager("images/worldmap.strf");
   
-  width  = 20;
-  height = 15;
-  
-  start_x = 4;
-  start_y = 5;
-
   tux = new Tux(this);
-  
-  leveldot_green = new Surface(datadir +  "/images/tiles/worldmap/leveldot_green.png", true);
-  leveldot_red = new Surface(datadir +  "/images/tiles/worldmap/leveldot_red.png", true);
-  messagedot   = new Surface(datadir +  "/images/tiles/worldmap/messagedot.png", true);
-  teleporterdot   = new Surface(datadir +  "/images/tiles/worldmap/teleporterdot.png", true);
+  add_object(tux);
+    
+  leveldot_green
+    = new Surface(datadir + "/images/tiles/worldmap/leveldot_green.png", true);
+  leveldot_red
+    = new Surface(datadir + "/images/tiles/worldmap/leveldot_red.png", true);
+  messagedot
+    = new Surface(datadir + "/images/tiles/worldmap/messagedot.png", true);
+  teleporterdot
+    = new Surface(datadir + "/images/tiles/worldmap/teleporterdot.png", true);
 
   name = "<no title>";
   music = "salcon.mod";
@@ -368,7 +369,12 @@ WorldMap::WorldMap()
 
 WorldMap::~WorldMap()
 {
-  delete tux;
+  clear_objects();
+  for(SpawnPoints::iterator i = spawn_points.begin();
+      i != spawn_points.end(); ++i) {
+    delete *i;
+  }
+    
   delete tile_manager;
 
   delete leveldot_green;
@@ -377,6 +383,29 @@ WorldMap::~WorldMap()
   delete teleporterdot;
 }
 
+void
+WorldMap::add_object(GameObject* object)
+{
+  TileMap* tilemap = dynamic_cast<TileMap*> (object);
+  if(tilemap != 0 && tilemap->is_solid()) {
+    solids = tilemap;
+  }
+
+  game_objects.push_back(object);
+}
+
+void
+WorldMap::clear_objects()
+{
+  for(GameObjects::iterator i = game_objects.begin();
+      i != game_objects.end(); ++i)
+    delete *i;
+  game_objects.clear();
+  solids = 0;
+  tux = new Tux(this);
+  add_object(tux);
+}
+
 // Don't forget to set map_filename before calling this
 void
 WorldMap::load_map()
@@ -392,23 +421,21 @@ WorldMap::load_map()
     if(!lisp)
       throw new std::runtime_error("file isn't a supertux-worldmap file.");
 
+    clear_objects();
     lisp::ListIterator iter(lisp);
     while(iter.next()) {
       if(iter.item() == "tilemap") {
-        if(tilemap.size() > 0)
-          throw new std::runtime_error("multiple tilemaps specified");
-        
-        const lisp::Lisp* tilemap_lisp = iter.lisp();
-        tilemap_lisp->get("width",  width);
-        tilemap_lisp->get("height", height);
-        tilemap_lisp->get_vector("tiles", tilemap);
+        add_object(new TileMap(*(iter.lisp()), tile_manager));
+      } else if(iter.item() == "background") {
+        add_object(new Background(*(iter.lisp())));
       } else if(iter.item() == "properties") {
         const lisp::Lisp* props = iter.lisp();
         props->get("name", name);
         props->get("music", music);
         props->get("intro-filename", intro_filename);
-        props->get("start_pos_x", start_x);
-        props->get("start_pos_y", start_y);
+      } else if(iter.item() == "spawnpoint") {
+        SpawnPoint* sp = new SpawnPoint(iter.lisp());
+        spawn_points.push_back(sp);
       } else if(iter.item() == "level") {
         parse_level_tile(iter.lisp());
       } else if(iter.item() == "special-tile") {
@@ -417,9 +444,20 @@ WorldMap::load_map()
         std::cerr << "Unknown token '" << iter.item() << "' in worldmap.\n";
       }
     }
+    if(solids == 0)
+      throw std::runtime_error("No solid tilemap specified");
+
+    // search for main spawnpoint
+    for(SpawnPoints::iterator i = spawn_points.begin();
+        i != spawn_points.end(); ++i) {
+      SpawnPoint* sp = *i;
+      if(sp->name == "main") {
+        Vector p = sp->pos;
+        tux->set_tile_pos(p);
+        break;
+      }
+    }
 
-    delete tux;
-    tux = new Tux(this);
   } catch(std::exception& e) {
     std::stringstream msg;
     msg << "Problem when parsing worldmap '" << map_filename << "': " <<
@@ -588,8 +626,8 @@ WorldMap::path_ok(Direction direction, Vector old_pos, Vector* new_pos)
 {
   *new_pos = get_next_tile(old_pos, direction);
 
-  if (!(new_pos->x >= 0 && new_pos->x < width
-        && new_pos->y >= 0 && new_pos->y < height))
+  if (!(new_pos->x >= 0 && new_pos->x < solids->get_width()
+        && new_pos->y >= 0 && new_pos->y < solids->get_height()))
     { // New position is outsite the tilemap
       return false;
     }
@@ -644,6 +682,24 @@ WorldMap::update(float delta)
     return;
   }
 
+  // update GameObjects
+  for(GameObjects::iterator i = game_objects.begin();
+      i != game_objects.end(); ++i) {
+    GameObject* object = *i;
+    object->update(delta);
+  }
+  // remove old GameObjects
+  for(GameObjects::iterator i = game_objects.begin();
+      i != game_objects.end(); ) {
+    GameObject* object = *i;
+    if(!object->is_valid()) {
+      delete object;
+      i = game_objects.erase(i);
+    } else {
+      ++i;
+    }
+  }
+  
   bool enter_level = false;
   if(main_controller->pressed(Controller::ACTION)
       || main_controller->pressed(Controller::JUMP)
@@ -725,7 +781,6 @@ WorldMap::update(float delta)
                     if (dir != D_NONE)
                       {
                         tux->set_direction(dir);
-                        //tux->update(delta);
                       }
                   }
               }
@@ -802,7 +857,6 @@ WorldMap::update(float delta)
     }
   else
     {
-      tux->update(delta);
 //      tux->set_direction(input_direction);
     }
 }
@@ -810,14 +864,7 @@ WorldMap::update(float delta)
 const Tile*
 WorldMap::at(Vector p)
 {
-  assert(p.x >= 0 
-         && p.x < width
-         && p.y >= 0
-         && p.y < height);
-
-  int x = int(p.x);
-  int y = int(p.y);
-  return tile_manager->get(tilemap[width * y + x]);
+  return solids->get_tile((int) p.x, (int) p.y);
 }
 
 WorldMap::Level*
@@ -847,13 +894,12 @@ WorldMap::at_special_tile()
 void
 WorldMap::draw(DrawingContext& context)
 {
-  for(int y = 0; y < height; ++y)
-    for(int x = 0; x < width; ++x)
-      {
-        const Tile* tile = at(Vector(x, y));
-        tile->draw(context, Vector(x*32, y*32), LAYER_TILES);
-      }
-
+  for(GameObjects::iterator i = game_objects.begin();
+      i != game_objects.end(); ++i) {
+    GameObject* object = *i;
+    object->draw(context);
+  }
+  
   for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
     {
       if (i->solved)
@@ -878,7 +924,6 @@ WorldMap::draw(DrawingContext& context)
                 Vector(i->pos.x*32, i->pos.y*32), LAYER_TILES+1);
     }
 
-  tux->draw(context);
   draw_status(context);
 }
 
@@ -887,38 +932,8 @@ WorldMap::draw_status(DrawingContext& context)
 {
   context.push_transform();
   context.set_translation(Vector(0, 0));
-  
-  char str[80];
-  sprintf(str, " %d", total_stats.get_points(SCORE_STAT));
-
-  context.draw_text(white_text, _("SCORE"), Vector(0, 0), LEFT_ALLIGN, LAYER_FOREGROUND1);
-  context.draw_text(gold_text, str, Vector(96, 0), LEFT_ALLIGN, LAYER_FOREGROUND1);
-
-  sprintf(str, "%d", player_status.coins);
-  context.draw_text(white_text, _("COINS"), Vector(SCREEN_WIDTH/2 - 16*5, 0),
-      LEFT_ALLIGN, LAYER_FOREGROUND1);
-  context.draw_text(gold_text, str, Vector(SCREEN_WIDTH/2 + (16*5)/2, 0),
-        LEFT_ALLIGN, LAYER_FOREGROUND1);
-
-  if (player_status.lives >= 5)
-    {
-      sprintf(str, "%dx", player_status.lives);
-      context.draw_text(gold_text, str, 
-          Vector(SCREEN_WIDTH - gold_text->get_text_width(str) - tux_life->w, 0),
-          LEFT_ALLIGN, LAYER_FOREGROUND1);
-      context.draw_surface(tux_life, Vector(SCREEN_WIDTH -
-            gold_text->get_text_width("9"), 0), LAYER_FOREGROUND1);
-    }
-  else
-    {
-      for(int i= 0; i < player_status.lives; ++i)
-        context.draw_surface(tux_life,
-            Vector(SCREEN_WIDTH - tux_life->w*4 + (tux_life->w*i), 0),
-            LAYER_FOREGROUND1);
-    }
-  context.draw_text(white_text, _("LIVES"),
-      Vector(SCREEN_WIDTH - white_text->get_text_width(_("LIVES")) - white_text->get_text_width("   99"), 0),
-      LEFT_ALLIGN, LAYER_FOREGROUND1);
+  player_status.draw(context);
 
   if (!tux->is_moving())
     {
@@ -985,23 +1000,26 @@ WorldMap::display()
     global_time += elapsed_time;
     lastticks = ticks;
     
-    // 40 fps minimum
+    // 40 fps minimum // TODO use same code as in GameSession here
     if(elapsed_time > .025)
       elapsed_time = .025;
     
     Vector tux_pos = tux->get_pos();
-    
-    offset.x = -tux_pos.x + SCREEN_WIDTH/2;
-    offset.y = -tux_pos.y + SCREEN_HEIGHT/2;
+    offset.x = tux_pos.x - SCREEN_WIDTH/2;
+    offset.y = tux_pos.y - SCREEN_HEIGHT/2;
 
-    if (offset.x > 0) offset.x = 0;
-    if (offset.y > 0) offset.y = 0;
+    if (offset.x < 0)
+      offset.x = 0;
+    if (offset.y < 0)
+      offset.y = 0;
+
+    if (offset.x > solids->get_width()*32 - SCREEN_WIDTH)
+      offset.x = solids->get_width()*32 - SCREEN_WIDTH;
+    if (offset.y > solids->get_height()*32 - SCREEN_HEIGHT)
+      offset.y = solids->get_height()*32 - SCREEN_HEIGHT;
 
-    if (offset.x < SCREEN_WIDTH - width*32) offset.x = SCREEN_WIDTH - width*32;
-    if (offset.y < SCREEN_HEIGHT - height*32) offset.y = SCREEN_HEIGHT - height*32;
-  
     context.push_transform();
-    context.set_translation(-offset);
+    context.set_translation(offset);
     draw(context);
     context.pop_transform();
     get_input();
@@ -1009,7 +1027,6 @@ WorldMap::display()
       
     if(Menu::current()) {
       Menu::current()->draw(context);
-      mouse_cursor->draw(context);
     }
 
     context.do_drawing();
@@ -1140,13 +1157,13 @@ WorldMap::loadgame(const std::string& filename)
           }
         } else {
           std::cerr << "Unknown token '" << iter.item() 
-            << "' in levels block in worldmap.\n";
+                    << "' in levels block in worldmap.\n";
         }
       }
     }
   } catch(std::exception& e) {
     std::cerr << "Problem loading game '" << filename << "': " << e.what() 
-      << "\n";
+              << "\n";
     load_map();
     player_status.reset();
   }
index dd5d03a..0894d28 100644 (file)
 #include "statistics.h"
 #include "timer.h"
 #include "tile_manager.h"
+#include "game_object.h"
 
 class Sprite;
 class Menu;
+class SpawnPoint;
+class GameObject;
+class TileMap;
 extern Menu* worldmap_menu;
 
 namespace WorldMapNS {
@@ -59,7 +63,7 @@ Direction reverse_dir(Direction d);
 
 class WorldMap;
 
-class Tux
+class Tux : public GameObject
 {
 public:
   Direction back_direction;
@@ -108,13 +112,10 @@ private:
   std::string name;
   std::string music;
 
-  std::vector<int> tilemap;
-  int width;
-  int height;
+  typedef std::vector<GameObject*> GameObjects;
+  GameObjects game_objects;
+  TileMap* solids;
   
-  int start_x;
-  int start_y;
-
   TileManager* tile_manager;
 
 public:
@@ -187,9 +188,10 @@ private:
 
   typedef std::vector<SpecialTile> SpecialTiles;
   SpecialTiles special_tiles;
-
   typedef std::vector<Level> Levels;
   Levels levels;
+  typedef std::vector<SpawnPoint*> SpawnPoints;
+  SpawnPoints spawn_points;
 
   MusicRef song;
 
@@ -219,6 +221,9 @@ public:
   
   void get_input();
 
+  void add_object(GameObject* object);
+  void clear_objects();
+
   /** Update Tux position */
   void update(float delta);
 
@@ -244,16 +249,10 @@ public:
   void loadmap(const std::string& filename);
 
   const std::string& get_world_title() const
-    { return name; }
+  { return name; }
     
-  const int& get_start_x() const
-    { return start_x; }
-  
-  const int& get_start_y() const
-    { return start_y; }
-
   void set_map_filename(std::string filename)
-    { map_filename = filename; }
+  { map_filename = filename; }
 
 private:
   void on_escape_press();