- Use obstacks for memory allocation for lispfiles and DrawingRequests,
[supertux.git] / src / object / camera.cpp
index ae6fecb..e4fd50a 100644 (file)
@@ -16,7 +16,6 @@
 //  You should have received a copy of the GNU General Public License
 //  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 <stdexcept>
 #include "path.hpp"
 #include "path_walker.hpp"
 
-Camera::Camera(Sector* newsector, std::string name) :
-  GameObject(name), mode(NORMAL), sector(newsector), do_backscrolling(true),
-  scrollchange(NONE)
+namespace {
+  enum CameraStyle { CameraStyleYI, CameraStyleKD, CameraStyleEXP };
+  const CameraStyle cameraStyle = CameraStyleYI;
+}
+
+Camera::Camera(Sector* newsector, std::string name)
+  : mode(NORMAL), sector(newsector), do_backscrolling(true),
+    scrollchange(NONE)
 {
+  this->name = name;
 }
 
 Camera::~Camera()
@@ -119,8 +124,25 @@ Camera::write(lisp::Writer& writer)
 }
 
 void
+Camera::reset_kd(const Vector& tuxpos)
+{
+  translation.x = tuxpos.x - (SCREEN_WIDTH * 0.5);
+  translation.y = tuxpos.y - (SCREEN_HEIGHT * 0.5);
+
+  shakespeed = 0;
+  shaketimer.stop();
+  keep_in_bounds(translation);
+}
+
+
+void
 Camera::reset(const Vector& tuxpos)
 {
+  if (cameraStyle == CameraStyleKD) {
+    reset_kd(tuxpos);
+    return;
+  }
+
   translation.x = tuxpos.x - SCREEN_WIDTH/3 * 2;
   translation.y = tuxpos.y - SCREEN_HEIGHT/2;
   shakespeed = 0;
@@ -149,7 +171,7 @@ Camera::scroll_to(const Vector& goal, float scrolltime)
   mode = SCROLLTO;
 }
 
-static const float EPSILON = .00001;
+static const float EPSILON = .00001f;
 static const float max_speed_y = 140;
 
 void
@@ -181,10 +203,14 @@ Camera::keep_in_bounds(Vector& translation)
     translation.y = height - SCREEN_HEIGHT;
   if(translation.y < 0)
     translation.y = 0;
+  if (height < SCREEN_HEIGHT)
+    translation.y = height/2.0 - SCREEN_HEIGHT/2.0;
   if(translation.x > width - SCREEN_WIDTH)
     translation.x = width - SCREEN_WIDTH;
   if(translation.x < 0)
     translation.x = 0;
+  if (width < SCREEN_WIDTH)
+    translation.x = width/2.0 - SCREEN_WIDTH/2.0;
 }
 
 void
@@ -197,8 +223,95 @@ Camera::shake()
 }
 
 void
+Camera::update_scroll_normal_kd(float elapsed_time)
+{
+  // make sure some time has actually passed
+  if(elapsed_time < EPSILON) return;
+
+  // make sure we have an active player
+  assert(sector != 0);
+  Player* player = sector->player;
+  Vector playerCenter = player->get_bbox().get_middle();
+
+  // If player is peeking, scroll in that direction
+  if (player->peeking_direction() == ::LEFT) {
+        translation.x -= elapsed_time * 128.0f;
+  }
+  else if (player->peeking_direction() == ::RIGHT) {
+        translation.x += elapsed_time * 128.0f;
+  }
+
+  // keep player within a small box, centered on the screen (vertical part)
+  bool do_y_scrolling = true;
+  if (player->is_dying() || sector->get_height() == 19*32) do_y_scrolling = false;
+  if (do_y_scrolling) {
+    translation.y = std::min(player->get_bbox().p1.y - SCREEN_HEIGHT * (0.5f - 0.17f), translation.y);
+    translation.y = std::max(player->get_bbox().p2.y - SCREEN_HEIGHT * (0.5f + 0.17f), translation.y);
+  }
+
+  // keep player within a small box, centered on the screen (horizontal part)
+  translation.x = std::min(player->get_bbox().p1.x - SCREEN_WIDTH * (0.5f - 0.1f), translation.x);
+  translation.x = std::max(player->get_bbox().p2.x - SCREEN_WIDTH * (0.5f + 0.1f), translation.x);
+
+  // make sure camera doesn't point outside level borders
+  keep_in_bounds(translation);
+
+  // handle shaking of camera (if applicable)
+  shake();
+}
+
+template<typename T>
+T clamp(T min, T max, T val)
+{
+  if(val < min)
+    return min;
+  if(val > max)
+    return max;
+
+  return val;
+}
+
+void
+Camera::update_scroll_normal_exp(float elapsed_time)
+{
+  static const Vector camera_speed = Vector(300, 100);
+
+  Player* player = sector->player;
+  const Vector& player_pos = player->get_bbox().get_middle();
+  static Vector last_player_pos = player_pos;
+  static Vector camera_delta = Vector(0, 0);
+
+  (void) elapsed_time;
+
+  Vector player_delta_x = player_pos - last_player_pos;
+  last_player_pos = player_pos;
+
+  Vector camera_delta_antic = Vector(0, 0) + player_delta_x * 25;
+  Vector myspeed = (camera_delta_antic - camera_delta) / elapsed_time;
+  myspeed.x = clamp(-camera_speed.x, camera_speed.x, myspeed.x);
+  myspeed.y = clamp(-camera_speed.y, camera_speed.y, myspeed.y);
+
+  camera_delta += myspeed * elapsed_time;
+
+  translation.x = camera_delta.x + player_pos.x - 0.5f * SCREEN_WIDTH;
+  translation.y = camera_delta.y + player_pos.y - 0.5f * SCREEN_HEIGHT;
+
+  keep_in_bounds(translation);
+  shake();
+}
+
+void
 Camera::update_scroll_normal(float elapsed_time)
 {
+  if (cameraStyle == CameraStyleEXP) {
+    update_scroll_normal_exp(elapsed_time);
+    return;
+  }
+  if (cameraStyle == CameraStyleKD) {
+    update_scroll_normal_kd(elapsed_time);
+    return;
+  }
+
   assert(sector != 0);
   Player* player = sector->player;
 
@@ -252,8 +365,19 @@ Camera::update_scroll_normal(float elapsed_time)
   // when suddenly changing directions while scrolling into the other direction.
   // abort scrolling, since tux might be going left/right at a relatively small
   // part of the map (like when jumping upwards)
-  if((player->dir == ::LEFT && scrollchange == RIGHT)
-      || (player->dir == ::RIGHT && scrollchange == LEFT))
+
+
+  // Find out direction in which the player walks: We want to try and show a
+  // bit more of what's in front of the player and less of what's behind
+  LeftRightScrollChange walkDirection;
+  if (player->physic.get_velocity_x() < -EPSILON) walkDirection = LEFT;
+  else if (player->physic.get_velocity_x() > EPSILON) walkDirection = RIGHT;
+  else if (player->dir == ::LEFT) walkDirection = LEFT;
+  else walkDirection = RIGHT;
+
+
+  if((walkDirection == LEFT && scrollchange == RIGHT)
+      || (walkDirection == RIGHT && scrollchange == LEFT))
     scrollchange = NONE;
   // when in left 1/3rd of screen scroll left
   if(player->get_bbox().get_middle().x < translation.x + SCREEN_WIDTH/3 - 16