* Add Airship (regular above-ground theme) and Battle (castle/boss theme) music court...
[supertux.git] / src / object / player.cpp
index 0cc997c..d09b97f 100644 (file)
 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include <config.h>
 
-#include <typeinfo>
-#include <cmath>
-#include <iostream>
-#include <cassert>
+#include "player.hpp"
 
-#include "gettext.hpp"
-#include "sprite/sprite_manager.hpp"
 #include "audio/sound_manager.hpp"
-#include "player.hpp"
-#include "tile.hpp"
-#include "sprite/sprite.hpp"
-#include "sector.hpp"
-#include "resources.hpp"
-#include "statistics.hpp"
+#include "badguy/badguy.hpp"
+#include "control/codecontroller.hpp"
+#include "control/joystickkeyboardcontroller.hpp"
+#include "display_effect.hpp"
+#include "log.hpp"
+#include "falling_coin.hpp"
 #include "game_session.hpp"
-#include "object/tilemap.hpp"
+#include "gettext.hpp"
+#include "main.hpp"
+#include "object/bullet.hpp"
 #include "object/camera.hpp"
-#include "object/particles.hpp"
 #include "object/portable.hpp"
-#include "object/bullet.hpp"
-#include "trigger/trigger_base.hpp"
-#include "control/joystickkeyboardcontroller.hpp"
-#include "scripting/squirrel_util.hpp"
-#include "main.hpp"
-#include "platform.hpp"
-#include "badguy/badguy.hpp"
+#include "object/sprite_particle.hpp"
+#include "object/tilemap.hpp"
+#include "particles.hpp"
 #include "player_status.hpp"
-#include "log.hpp"
-#include "falling_coin.hpp"
 #include "random_generator.hpp"
-#include "object/sprite_particle.hpp"
+#include "sector.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "sprite/sprite.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "tile.hpp"
 #include "trigger/climbable.hpp"
 
+#include <typeinfo>
+#include <cmath>
+#include <iostream>
+#include <cassert>
+
 //#define SWIMMING
 
 namespace {
@@ -94,6 +93,12 @@ namespace {
   /** instant velocity when tux starts to walk */
   static const float WALK_SPEED = 100;
 
+  /** multiplied by WALK_ACCELERATION to give friction */
+  static const float NORMAL_FRICTION_MULTIPLIER = 1.5f;
+  /** multiplied by WALK_ACCELERATION to give friction */
+  static const float ICE_FRICTION_MULTIPLIER = 0.1f;
+  static const float ICE_ACCELERATION_MULTIPLIER = 0.25f;
+
   /** time of the kick (kicking mriceblock) animation */
   static const float KICK_TIME = .3f;
   /** time of tux cheering (currently unused) */
@@ -105,6 +110,8 @@ namespace {
       the apex of the jump is reached */
   static const float JUMP_EARLY_APEX_FACTOR = 3.0;
 
+  static const float JUMP_GRACE_TIME = 0.25f; /**< time before hitting the ground that the jump button may be pressed (and still trigger a jump) */
+
   bool no_water = true;
 }
 
@@ -169,6 +176,8 @@ Player::init()
   backflip_direction = 0;
   visible = true;
   swimming = false;
+  on_ice = false;
+  ice_this_frame = false;
   speedlimit = 0; //no special limit
 
   on_ground_flag = false;
@@ -348,7 +357,11 @@ Player::update(float elapsed_time)
     grabbed_object = NULL;
   }
 
+  if(!ice_this_frame && on_ground())
+    on_ice = false;
+
   on_ground_flag = false;
+  ice_this_frame = false;
 
   // when invincible, spawn particles
   if (invincible_timer.started() && !dying)
@@ -359,17 +372,13 @@ Player::update(float elapsed_time)
       Vector ppos = Vector(px, py);
       Vector pspeed = Vector(0, 0);
       Vector paccel = Vector(0, 0);
-      // draw bright sparkle when there is lots of time left, dark sparkle when invincibility is about to end
-      if (invincible_timer.get_timeleft() > TUX_INVINCIBLE_TIME_WARNING) {
-        // make every other a longer sparkle to make trail a bit fuzzy
-        if (size_t(game_time*20)%2) {
-          Sector::current()->add_object(new SpriteParticle("images/objects/particles/sparkle.sprite", "small", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS+1+5));
-        } else {
-          Sector::current()->add_object(new SpriteParticle("images/objects/particles/sparkle.sprite", "medium", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS+1+5));
-        }
-      } else {
-        Sector::current()->add_object(new SpriteParticle("images/objects/particles/sparkle.sprite", "dark", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS+1+5));
-      }
+      Sector::current()->add_object(new SpriteParticle("images/objects/particles/sparkle.sprite", 
+         // draw bright sparkle when there is lots of time left, dark sparkle when invincibility is about to end
+         (invincible_timer.get_timeleft() > TUX_INVINCIBLE_TIME_WARNING) ?
+              // make every other a longer sparkle to make trail a bit fuzzy
+              (size_t(game_time*20)%2) ? "small" : "medium"
+         :
+              "dark", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS+1+5));
     }
   }
 
@@ -400,10 +409,13 @@ Player::apply_friction()
   if ((on_ground()) && (fabs(physic.get_velocity_x()) < WALK_SPEED)) {
     physic.set_velocity_x(0);
     physic.set_acceleration_x(0);
-  } else if(physic.get_velocity_x() < 0) {
-    physic.set_acceleration_x(WALK_ACCELERATION_X * 1.5);
+  } else {
+    float friction = WALK_ACCELERATION_X * (on_ice ? ICE_FRICTION_MULTIPLIER : NORMAL_FRICTION_MULTIPLIER);
+    if(physic.get_velocity_x() < 0) {
+      physic.set_acceleration_x(friction);
     } else if(physic.get_velocity_x() > 0) {
-    physic.set_acceleration_x(WALK_ACCELERATION_X * -1.5);
+      physic.set_acceleration_x(-friction);
+    } // no friction for physic.get_velocity_x() == 0
   }
 }
 
@@ -488,6 +500,10 @@ Player::handle_horizontal_input()
     }
   }
 
+  if(on_ice) {
+    ax *= ICE_ACCELERATION_MULTIPLIER;
+  }
+
   physic.set_velocity(vx, vy);
   physic.set_acceleration(ax, ay);
 
@@ -608,7 +624,9 @@ void
 Player::handle_vertical_input()
 {
   // Press jump key
-  if(controller->pressed(Controller::JUMP) && (can_jump)) {
+  if(controller->pressed(Controller::JUMP)) jump_button_timer.start(JUMP_GRACE_TIME);
+  if(controller->hold(Controller::JUMP) && jump_button_timer.started() && can_jump) {
+    jump_button_timer.stop();
     if (duck) {
       // when running, only jump a little bit; else do a backflip
       if ((physic.get_velocity_x() != 0) || (controller->hold(Controller::LEFT)) || (controller->hold(Controller::RIGHT))) do_jump(-300); else do_backflip();
@@ -666,16 +684,10 @@ Player::handle_input()
   }
 
   /* Peeking */
-  if( controller->released( Controller::PEEK_LEFT ) ) {
-    peekingX = AUTO;
-  }
-  if( controller->released( Controller::PEEK_RIGHT ) ) {
+  if( controller->released( Controller::PEEK_LEFT ) || controller->released( Controller::PEEK_RIGHT ) ) {
     peekingX = AUTO;
   }
-  if( controller->released( Controller::PEEK_UP ) ) {
-    peekingY = AUTO;
-  }
-  if( controller->released( Controller::PEEK_DOWN ) ) {
+  if( controller->released( Controller::PEEK_UP ) || controller->released( Controller::PEEK_DOWN ) ) {
     peekingY = AUTO;
   }
   if( controller->pressed( Controller::PEEK_LEFT ) ) {
@@ -696,7 +708,7 @@ Player::handle_input()
   if (!backflipping) handle_horizontal_input();
 
   /* Jump/jumping? */
-  if (on_ground() && !controller->hold(Controller::JUMP))
+  if (on_ground())
     can_jump = true;
 
   /* Handle vertical movement: */
@@ -1067,6 +1079,11 @@ Player::collision_tile(uint32_t tile_attributes)
     }
   }
 #endif
+
+  if(tile_attributes & Tile::ICE) {
+    ice_this_frame = true;
+    on_ice = true;
+  }
 }
 
 void
@@ -1224,9 +1241,7 @@ Player::kill(bool completely)
     dying_timer.start(3.0);
     set_group(COLGROUP_DISABLED);
 
-    DisplayEffect* effect = new DisplayEffect();
-    effect->fade_out(3.0);
-    Sector::current()->add_object(effect);
+    Sector::current()->effect->fade_out(3.0);
     sound_manager->stop_music(3.0);
   }
 }
@@ -1251,13 +1266,19 @@ Player::move(const Vector& vector)
 void
 Player::check_bounds(Camera* camera)
 {
-  /* Keep tux in bounds: */
+  /* Keep tux in sector bounds: */
   if (get_pos().x < 0) {
-    // Lock Tux to the size of the level, so that he doesn't fall of
-    // on the left side
+    // Lock Tux to the size of the level, so that he doesn't fall off
+    // the left side
     set_pos(Vector(0, get_pos().y));
   }
 
+  if (get_bbox().get_right() > Sector::current()->get_width()) {
+    // Lock Tux to the size of the level, so that he doesn't fall off
+    // the right side
+    set_pos(Vector(Sector::current()->get_width() - get_bbox().get_width(), get_pos().y));
+  }
+
   /* fallen out of the level? */
   if ((get_pos().y > Sector::current()->get_height()) && (!ghost_mode)) {
     kill(true);