implemented autoscrolling and proper camera parsing in levelfiles
authorMatthias Braun <matze@braunis.de>
Tue, 25 May 2004 00:28:24 +0000 (00:28 +0000)
committerMatthias Braun <matze@braunis.de>
Tue, 25 May 2004 00:28:24 +0000 (00:28 +0000)
SVN-Revision: 1320

src/Makefile.am
src/camera.cpp
src/camera.h
src/level.cpp
src/level.h
src/player.cpp
src/player.h
src/vector.cpp [new file with mode: 0644]
src/vector.h
src/world.cpp

index 23427d0..8c230e7 100644 (file)
@@ -90,6 +90,8 @@ tilemap.h \
 tilemap.cpp \
 moving_object.h \
 moving_object.cpp \
-serializable.h
+serializable.h \
+vector.cpp \
+vector.h
 
 # EOF #
index 6ef8168..c25cc20 100644 (file)
@@ -18,6 +18,8 @@
 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "camera.h"
 
+#include <stdexcept>
+#include <sstream>
 #include <math.h>
 #include "lispwriter.h"
 #include "player.h"
@@ -25,7 +27,8 @@
 #include "globals.h"
 
 Camera::Camera(Player* newplayer, Level* newlevel)
-  : player(newplayer), level(newlevel), scrollchange(NONE)
+  : player(newplayer), level(newlevel), do_backscrolling(true),
+    scrollchange(NONE), auto_idx(0), auto_t(0)
 {
   if(!player || !level)
     mode = MANUAL;
@@ -44,14 +47,74 @@ Camera::set_translation(const Vector& newtranslation)
 }
 
 void
+Camera::read(LispReader& reader)
+{
+  std::string modename;
+  
+  reader.read_string("mode", &modename);
+  if(modename == "normal") {
+    mode = NORMAL;
+
+    do_backscrolling = true;
+    reader.read_bool("backscrolling", &do_backscrolling);
+  } else if(modename == "autoscroll") {
+    mode = AUTOSCROLL;
+    
+    lisp_object_t* cur = 0;
+    reader.read_lisp("path", &cur);
+    if(cur == 0) {
+      throw std::runtime_error("No path specified in autoscroll camera.");
+    }
+    float speed = .5;
+    while(!lisp_nil_p(cur)) {
+      if(strcmp(lisp_symbol(lisp_car(lisp_car(cur))), "point") != 0) {
+        std::cerr << "Warning: unknown token in camera path.\n";
+        continue;
+      }
+           
+      LispReader reader(lisp_cdr(lisp_car(cur)));
+
+      ScrollPoint point;
+      if(!reader.read_float("x", &point.position.x) ||
+         !reader.read_float("y", &point.position.y)) {
+        throw std::runtime_error("x and y missing in point of camerapath");
+      }
+      reader.read_float("speed", &speed);
+      point.speed = speed;
+      scrollpoints.push_back(point);
+
+      cur = lisp_cdr(cur);
+    }
+  } else if(modename == "manual") {
+    mode = MANUAL;
+  } else {
+    std::stringstream str;
+    str << "invalid camera mode '" << modename << "'found in worldfile.";
+    throw std::runtime_error(str.str());
+  }
+}
+
+void
 Camera::write(LispWriter& writer)
 {
   writer.start_list("camera");
   
   if(mode == NORMAL) {
     writer.write_string("mode", "normal");
+    writer.write_bool("backscrolling", do_backscrolling);
   } else if(mode == AUTOSCROLL) {
     writer.write_string("mode", "autoscroll");
+    writer.start_list("path");
+    for(std::vector<ScrollPoint>::iterator i = scrollpoints.begin();
+        i != scrollpoints.end(); ++i) {
+      writer.start_list("point");
+      writer.write_float("x", i->position.x);
+      writer.write_float("y", i->position.y);
+      writer.write_float("speed", i->speed);
+      writer.end_list("point");
+    }
+
+    writer.end_list("path");
   } else if(mode == MANUAL) {
     writer.write_string("mode", "manual");
   }
@@ -72,6 +135,20 @@ Camera::action(float elapsed_time)
 }
 
 void
+Camera::keep_in_bounds()
+{
+  // don't scroll before the start or after the level's end
+  if(translation.y > level->height * 32 - screen->h)
+    translation.y = level->height * 32 - screen->h;
+  if(translation.y < 0)                                      
+    translation.y = 0; 
+  if(translation.x > level->width * 32 - screen->w)
+    translation.x = level->width * 32 - screen->w;
+  if(translation.x < 0)
+    translation.x = 0;                                         
+}
+
+void
 Camera::scroll_normal(float elapsed_time)
 {
   assert(level != 0 && player != 0);
@@ -112,12 +189,6 @@ Camera::scroll_normal(float elapsed_time)
 
     // finally scroll with calculated speed
     translation.y -= speed_y * elapsed_time;
-
-    // don't scroll before the start or after the level's end
-    if(translation.y > level->height * 32 - screen->h)
-      translation.y = level->height * 32 - screen->h;
-    if(translation.y < 0)
-      translation.y = 0; 
   }
 
   /****** Horizontal scrolling part *******/
@@ -131,7 +202,7 @@ Camera::scroll_normal(float elapsed_time)
       || (player->dir == ::RIGHT && scrollchange == LEFT))
     scrollchange = NONE;
   // when in left 1/3rd of screen scroll left
-  if(player->base.x < translation.x + screen->w/3 && level->back_scrolling)
+  if(player->base.x < translation.x + screen->w/3 && do_backscrolling)
     scrollchange = LEFT;
   // scroll right when in right 1/3rd of screen
   else if(player->base.x > translation.x + screen->w/3*2)
@@ -161,15 +232,39 @@ Camera::scroll_normal(float elapsed_time)
   // apply scrolling
   translation.x -= speed_x * elapsed_time;
 
-  // don't scroll before the start or after the level's end
-  if(translation.x > level->width * 32 - screen->w)
-    translation.x = level->width * 32 - screen->w;
-  if(translation.x < 0)
-    translation.x = 0;   
+  keep_in_bounds();
 }
 
 void
 Camera::scroll_autoscroll(float elapsed_time)
 {
-  // TODO
+  if(player->dying)
+    return;
+
+  if(auto_t - elapsed_time >= 0) {
+    translation += current_dir * elapsed_time;
+    auto_t -= elapsed_time;
+  } else {
+    // do the rest of the old movement
+    translation += current_dir * auto_t;
+    elapsed_time -= auto_t;
+    auto_t = 0;
+
+    // construct path for next point
+    if(auto_idx+1 >= scrollpoints.size()) {
+      keep_in_bounds();
+      return;
+    }
+    Vector distance = scrollpoints[auto_idx+1].position 
+                      - scrollpoints[auto_idx].position;
+    current_dir = distance.unit() * scrollpoints[auto_idx].speed;
+    auto_t = distance.norm() / scrollpoints[auto_idx].speed;
+
+    // do movement for the remaining time
+    translation += current_dir * elapsed_time;
+    auto_t -= elapsed_time;
+    auto_idx++;
+  }
+
+  keep_in_bounds();
 }
index fd37ed9..453a06a 100644 (file)
@@ -19,6 +19,7 @@
 #ifndef __VIEWPORT_H__
 #define __VIEWPORT_H__
 
+#include <vector>
 #include "vector.h"
 #include "game_object.h"
 #include "serializable.h"
@@ -44,7 +45,7 @@ public:
   }                                                  
 
   /// parse camera mode from lisp file
-  void parse_camera(LispReader& reader);
+  void read(LispReader& reader);
   /// write camera mode to a lisp file
   virtual void write(LispWriter& writer);
 
@@ -60,10 +61,12 @@ public:
   {
     NORMAL, AUTOSCROLL, MANUAL
   };
+  CameraMode mode;
 
 private:
   void scroll_normal(float elapsed_time);
   void scroll_autoscroll(float elapsed_time);
+  void keep_in_bounds();
 
   enum LeftRightScrollChange
   {
@@ -74,12 +77,21 @@ private:
 
   Player* player;
   Level* level;
-  CameraMode mode;
 
   // normal mode
+  bool do_backscrolling;
   LeftRightScrollChange scrollchange;
 
   // autoscroll mode
+  class ScrollPoint {
+  public:
+    Vector position;
+    float speed;
+  };
+  std::vector<ScrollPoint> scrollpoints;
+  size_t auto_idx;
+  float auto_t;
+  Vector current_dir;
 };
 
 #endif
index 3a55398..82c1d62 100644 (file)
@@ -223,8 +223,8 @@ Level::init_defaults()
   bkgd_image = "arctis.jpg";
   width      = 0;
   height     = 0;
-  start_pos_x = 100;
-  start_pos_y = 170;
+  start_pos.x = 100;
+  start_pos.y = 170;
   time_left  = 100;
   gravity    = 10.;
   back_scrolling = false;
@@ -277,8 +277,8 @@ Level::load(const std::string& filename, World* world)
       reader.read_int("version",  &version);
       if(!reader.read_int("width",  &width))
         st_abort("No width specified for level.", "");
-      if (!reader.read_int("start_pos_x", &start_pos_x)) start_pos_x = 100;
-      if (!reader.read_int("start_pos_y", &start_pos_y)) start_pos_y = 170;
+      if (!reader.read_float("start_pos_x", &start_pos.x)) start_pos.x = 100;
+      if (!reader.read_float("start_pos_y", &start_pos.y)) start_pos.y = 170;
       time_left = 500;
       if(!reader.read_int("time",  &time_left)) {
         printf("Warning no time specified for level.\n");
@@ -355,7 +355,7 @@ Level::load(const std::string& filename, World* world)
           }
       }
 
-      { // Read BadGuys
+      { // Read Objects
         lisp_object_t* cur = 0;
         if (reader.read_lisp("objects",  &cur))
           {
@@ -364,77 +364,15 @@ Level::load(const std::string& filename, World* world)
           }
       }
 
-#if 0 // TODO fix this or remove it
-      // Convert old levels to the new tile numbers
-      if (version == 0)
-        {
-          std::map<char, int> transtable;
-          transtable['.'] = 0;
-          transtable['x'] = 104;
-          transtable['X'] = 77;
-          transtable['y'] = 78;
-          transtable['Y'] = 105;
-          transtable['A'] = 83;
-          transtable['B'] = 102;
-          transtable['!'] = 103;
-          transtable['a'] = 84;
-          transtable['C'] = 85;
-          transtable['D'] = 86;
-          transtable['E'] = 87;
-          transtable['F'] = 88;
-          transtable['c'] = 89;
-          transtable['d'] = 90;
-          transtable['e'] = 91;
-          transtable['f'] = 92;
-
-          transtable['G'] = 93;
-          transtable['H'] = 94;
-          transtable['I'] = 95;
-          transtable['J'] = 96;
-
-          transtable['g'] = 97;
-          transtable['h'] = 98;
-          transtable['i'] = 99;
-          transtable['j'] = 100
-                            ;
-          transtable['#'] = 11;
-          transtable['['] = 13;
-          transtable['='] = 14;
-          transtable[']'] = 15;
-          transtable['$'] = 82;
-          transtable['^'] = 76;
-          transtable['*'] = 80;
-          transtable['|'] = 79;
-          transtable['\\'] = 81;
-          transtable['&'] = 75;
-
-          int x = 0;
-          int y = 0;
-          for(std::vector<int>::iterator i = ia_tm.begin(); i != ia_tm.end(); ++i)
-            {
-              if (*i == '0' || *i == '1' || *i == '2')
-                {
-                  badguy_data.push_back(BadGuyData(static_cast<BadGuyKind>(*i-'0'),
-                                                   x*32, y*32, false));
-                  *i = 0;
-                }
-              else
-                {
-                  std::map<char, int>::iterator j = transtable.find(*i);
-                  if (j != transtable.end())
-                    *i = j->second;
-                  else
-                    printf("Error: conversion will fail, unsupported char: '%c' (%d)\n", *i, *i);
-                }
-              ++x;
-              if (x >= width)
-                {
-                  x = 0;
-                  ++y;
-                }
-            }
-        }
-#endif
+      { // Read Camera
+        lisp_object_t* cur = 0;
+        if (reader.read_lisp("camera", &cur))
+          {
+            LispReader reader(cur);
+            if(world)
+              world->camera->read(reader);
+          }
+      }
     }
 
   lisp_free(root_obj);
index 1d2b678..70015fc 100644 (file)
@@ -92,8 +92,7 @@ class Level
   int width;
   int height;
   int bkgd_speed;
-  int start_pos_x;
-  int start_pos_y;
+  Vector start_pos;
   float gravity;
   bool back_scrolling;
   float hor_autoscroll_speed;
index fde8292..18539a4 100644 (file)
@@ -83,8 +83,6 @@ Player::~Player()
 void
 Player::init()
 {
-  Level* plevel = World::current()->get_level();
-
   holding_something = false;
 
   base.width = 32;
@@ -93,12 +91,13 @@ Player::init()
   size = SMALL;
   got_power = NONE_POWER;
 
-  base.x = plevel->start_pos_x;
-  base.y = plevel->start_pos_y;
+  base.x = 0;
+  base.y = 0;
   previous_base = old_base = base;
   dir = RIGHT;
   old_dir = dir;
   duck = false;
+  dead = false;
 
   dying   = DYING_NOT;
   last_ground_y = 0;
@@ -181,6 +180,11 @@ Player::action(float elapsed_time)
 {
   bool jumped_in_solid = false;
 
+  if(dying && !dying_timer.check()) {
+    dead = true;
+    return;
+  }
+
   if (input.fire == UP)
     holding_something = false;
 
@@ -831,6 +835,9 @@ Player::collision(void* p_c_object, int c_object)
 void
 Player::kill(HurtMode mode)
 {
+  if(dying)
+    return;
+  
   play_sound(sounds[SND_HURT], SOUND_CENTER_SPEAKER);
 
   physic.set_velocity_x(0);
@@ -854,32 +861,12 @@ Player::kill(HurtMode mode)
       physic.enable_gravity(true);
       physic.set_acceleration(0, 0);
       physic.set_velocity(0, 7);
-      if(dying != DYING_SQUISHED)
       --player_status.lives;
       dying = DYING_SQUISHED;
+      dying_timer.start(3000);
     }
 }
 
-void
-Player::is_dying()
-{
-  remove_powerups();
-  dying = DYING_NOT;
-}
-
-bool Player::is_dead()
-{
-  float scroll_x =
-    World::current()->camera->get_translation().x;
-  float scroll_y =
-    World::current()->camera->get_translation().y;
-  if(base.y > screen->h + scroll_y || base.y > World::current()->get_level()->height*32 ||
-      base.x < scroll_x - AUTOSCROLL_DEAD_INTERVAL)  // can happen in auto-scrolling
-    return true;
-  else
-    return false;
-}
-
 /* Remove Tux's power ups */
 void
 Player::remove_powerups()
@@ -890,6 +877,14 @@ Player::remove_powerups()
 }
 
 void
+Player::move(const Vector& vector)
+{
+  base.x = vector.x;
+  base.y = vector.y;
+  old_base = previous_base = base;
+}
+
+void
 Player::check_bounds(Camera& viewport,
     bool back_scrolling, bool hor_autoscroll)
 {
@@ -904,10 +899,27 @@ Player::check_bounds(Camera& viewport,
   if (base.y > World::current()->get_level()->height * /*TILE_HEIGHT*/ 32)
     {
       kill(KILL);
+      return;
     }
 
-  if(base.x < viewport.get_translation().x && (!back_scrolling || hor_autoscroll))  // can happen if back scrolling is disabled
+  bool adjust = false;
+  // can happen if back scrolling is disabled
+  if(base.x < viewport.get_translation().x) {
     base.x = viewport.get_translation().x;
+    adjust = true;
+  }
+  if(base.x >= viewport.get_translation().x + screen->w - base.width) {
+    base.x = viewport.get_translation().x + screen->w - base.width;
+    adjust = true;
+  }
+
+  if(adjust) {
+    // squished now?
+    if(collision_object_map(base)) {
+      kill(KILL);
+      return;
+    }
+  }
 
   if(hor_autoscroll)
     {
index 04473f2..6852ebc 100644 (file)
@@ -119,6 +119,7 @@ public:
   int size;
   bool duck;
   bool holding_something;
+  bool dead;
   DyingType dying;
 
   Direction dir;
@@ -140,6 +141,7 @@ public:
   Timer frame_timer;
   Timer kick_timer;
   Timer shooting_timer;   // used to show the arm when Tux is shooting
+  Timer dying_timer;
   Physic physic;
 
 public:
@@ -158,14 +160,15 @@ public:
 
   void collision(void* p_c_object, int c_object);
   void kill(HurtMode mode);
-  void is_dying();
-  bool is_dead();
   void player_remove_powerups();
   void check_bounds(Camera& viewport, bool back_scrolling, bool hor_autoscroll);
   bool on_ground();
   bool under_solid();
   bool tiles_on_air(int tiles);
   void grow();
+  void move(const Vector& vector);
+  bool is_dead() const
+  { return dead; }
   
 private:
   void init();
diff --git a/src/vector.cpp b/src/vector.cpp
new file mode 100644 (file)
index 0000000..de3e53c
--- /dev/null
@@ -0,0 +1,12 @@
+#include "vector.h"
+#include <math.h>
+
+Vector Vector::unit() const
+{
+  return *this / norm();
+}
+
+float Vector::norm() const
+{
+  return sqrt(x*x + y*y);
+}
index 6436c3f..21f4a6a 100644 (file)
@@ -41,6 +41,11 @@ public:
     return Vector(x * s, y * s);
   }
 
+  Vector operator/(float s) const
+  {
+    return Vector(x / s, y / s);
+  }
+
   const Vector& operator +=(const Vector& other)
   {
     x += other.x;
@@ -48,6 +53,15 @@ public:
     return *this;
   }
 
+  // scalar product of 2 vectors
+  float operator*(const Vector& other) const
+  {
+    return x*other.x + y*other.y;
+  }
+
+  float norm() const;
+  Vector unit() const;
+
   // ... add the other operators as needed, I'm too lazy now ...
 
   float x, y; // leave this public, get/set methods just give me headaches
index 9ba5d18..0c28cb0 100644 (file)
@@ -48,16 +48,20 @@ World::World(const std::string& filename, int level_nr)
   // world calls child functions
   current_ = this;
 
+  tux = new Player(displaymanager);
+  add_object(tux);
+  
   level = new Level();
+  camera = new Camera(tux, level);
+  add_object(camera);                 
+
   if(level_nr >= 0) {
     level->load(filename, level_nr, this);
   } else {
     level->load(filename, this);
   }
-
-  tux = new Player(displaymanager);
-  add_object(tux);
-
+  tux->move(level->start_pos);
+  
   set_defaults();
 
   level->load_gfx();
@@ -75,9 +79,6 @@ World::World(const std::string& filename, int level_nr)
   add_object(new TileMap(displaymanager, level));
   level->load_song();
 
-  camera = new Camera(tux, level);
-  add_object(camera);               
-
   apply_bonuses();
 }