Merged back changes from 0.3.x branch
[supertux.git] / src / object / player.cpp
index a80ea94..8367bd6 100644 (file)
@@ -108,14 +108,15 @@ TuxBodyParts::draw(DrawingContext& context, const Vector& pos, int layer)
     feet->draw(context, pos, layer-2);
 }
 
-Player::Player(PlayerStatus* _player_status, std::string name) :
-  MovingObject(name), player_status(_player_status), grabbed_object(NULL),
-  ghost_mode(false)
+Player::Player(PlayerStatus* _player_status, const std::string& name)
+  : player_status(_player_status), grabbed_object(NULL), ghost_mode(false)
 {
+  this->name = name;
   controller = main_controller;
   smalltux_gameover = sprite_manager->create("images/creatures/tux_small/smalltux-gameover.sprite");
   smalltux_star = sprite_manager->create("images/creatures/tux_small/smalltux-star.sprite");
   bigtux_star = sprite_manager->create("images/creatures/tux_big/bigtux-star.sprite");
+  airarrow.reset(new Surface("images/engine/hud/airarrow.png"));
 
   sound_manager->preload("sounds/bigjump.wav");
   sound_manager->preload("sounds/jump.wav");
@@ -171,14 +172,18 @@ Player::init()
 void
 Player::expose(HSQUIRRELVM vm, SQInteger table_idx)
 {
-  if (name.empty()) return;
+  if (name.empty())
+    return;
+
   Scripting::expose_object(vm, table_idx, dynamic_cast<Scripting::Player *>(this), name, false);
 }
 
 void
 Player::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
 {
-  if (name.empty()) return;
+  if (name.empty())
+    return;
+
   Scripting::unexpose_object(vm, table_idx, name);
 }
 
@@ -198,7 +203,7 @@ Player::adjust_height(float new_height)
   if(new_height > bbox.get_height()) {
     Rect additional_space = bbox2;
     additional_space.set_height(new_height - bbox.get_height());
-    if(!Sector::current()->is_free_space(additional_space))
+    if(!Sector::current()->is_free_of_statics(additional_space, this))
       return false;
   }
 
@@ -226,7 +231,8 @@ Player::update(float elapsed_time)
     handle_input();
 
   // handle_input() calls apply_friction() when Tux is not walking, so we'll have to do this ourselves
-  if (deactivated) apply_friction();
+  if (deactivated)
+    apply_friction();
 
   // extend/shrink tux collision rectangle so that we fall through/walk over 1
   // tile holes
@@ -520,7 +526,7 @@ Player::handle_vertical_input()
   if(controller->pressed(Controller::JUMP) && (can_jump)) {
     if (duck) {
       // when running, only jump a little bit; else do a backflip
-      if (physic.get_velocity_x() != 0) do_jump(-300); else do_backflip();
+      if ((physic.get_velocity_x() != 0) || (controller->hold(Controller::LEFT)) || (controller->hold(Controller::RIGHT))) do_jump(-300); else do_backflip();
     } else {
       // jump a bit higher if we are running; else do a normal jump
       if (fabs(physic.get_velocity_x()) > MAX_WALK_XM) do_jump(-580); else do_jump(-520);
@@ -547,7 +553,8 @@ Player::handle_vertical_input()
   // swimming
   physic.set_acceleration_y(0);
   if (swimming) {
-    if (controller->hold(Controller::UP) || controller->hold(Controller::JUMP)) physic.set_acceleration_y(-2000);
+    if (controller->hold(Controller::UP) || controller->hold(Controller::JUMP))
+      physic.set_acceleration_y(-2000);
     physic.set_velocity_y(physic.get_velocity_y() * 0.94);
   }
 }
@@ -560,24 +567,6 @@ Player::handle_input()
     return;
   }
 
-  if(!controller->hold(Controller::ACTION) && grabbed_object) {
-    // move the grabbed object a bit away from tux
-    Vector pos = get_pos() +
-        Vector(dir == LEFT ? -bbox.get_width()-1 : bbox.get_width()+1,
-                bbox.get_height()*0.66666 - 32);
-    Rect dest(pos, pos + Vector(32, 32));
-    if(Sector::current()->is_free_space(dest)) {
-      MovingObject* moving_object = dynamic_cast<MovingObject*> (grabbed_object);
-      if(moving_object) {
-        moving_object->set_pos(pos);
-      } else {
-        log_debug << "Non MovingObjetc grabbed?!?" << std::endl;
-      }
-      grabbed_object->ungrab(*this, dir);
-      grabbed_object = NULL;
-    }
-  }
-
   /* Peeking */
   if( controller->released( Controller::PEEK_LEFT ) ) {
     peeking = AUTO;
@@ -603,7 +592,7 @@ Player::handle_input()
   handle_vertical_input();
 
   /* Shoot! */
-  if (controller->pressed(Controller::ACTION) && player_status->bonus == FIRE_BONUS) {
+  if (controller->pressed(Controller::ACTION) && (player_status->bonus == FIRE_BONUS || player_status->bonus == ICE_BONUS)) {
     if(Sector::current()->add_bullet(
          get_pos() + ((dir == LEFT)? Vector(0, bbox.get_height()/2)
                       : Vector(32, bbox.get_height()/2)),
@@ -612,8 +601,65 @@ Player::handle_input()
   }
 
   /* Duck or Standup! */
-  if (controller->hold(Controller::DOWN)) do_duck(); else do_standup();
+  if (controller->hold(Controller::DOWN)) {
+    do_duck();
+  } else {
+    do_standup();
+  }
+
+  /* grabbing */
+  try_grab();
 
+  if(!controller->hold(Controller::ACTION) && grabbed_object) {
+    // move the grabbed object a bit away from tux
+    Vector pos = get_pos() +
+        Vector(dir == LEFT ? -bbox.get_width()-1 : bbox.get_width()+1,
+                bbox.get_height()*0.66666 - 32);
+    Rect dest(pos, pos + Vector(32, 32));
+    if(Sector::current()->is_free_of_statics(dest)) {
+      MovingObject* moving_object = dynamic_cast<MovingObject*> (grabbed_object);
+      if(moving_object) {
+        moving_object->set_pos(pos);
+      } else {
+        log_debug << "Non MovingObjetc grabbed?!?" << std::endl;
+      }
+      grabbed_object->ungrab(*this, dir);
+      grabbed_object = NULL;
+    }
+  }
+}
+
+void 
+Player::try_grab()
+{
+  if(controller->hold(Controller::ACTION) && !grabbed_object
+      && !duck) {
+  Sector* sector = Sector::current();
+    Vector pos;
+    if(dir == LEFT) {
+      pos = Vector(bbox.get_left() - 5, bbox.get_bottom() - 16);
+    } else {
+      pos = Vector(bbox.get_right() + 5, bbox.get_bottom() - 16);
+    }
+
+    for(Sector::Portables::iterator i = sector->portables.begin();
+        i != sector->portables.end(); ++i) {
+      Portable* portable = *i;
+      if(!portable->is_portable())
+        continue;
+
+      MovingObject* moving_object = dynamic_cast<MovingObject*> (portable);
+      assert(portable);
+      if(moving_object == NULL)
+        continue;
+
+      if(moving_object->get_bbox().contains(pos)) {
+        grabbed_object = portable;
+        grabbed_object->grab(*this, get_pos(), dir);
+        break;
+      }
+    }
+  }
 }
 
 void
@@ -648,6 +694,12 @@ Player::add_coins(int count)
   player_status->add_coins(count);
 }
 
+int
+Player::get_coins()
+{
+  return player_status->coins;
+}
+
 bool
 Player::add_bonus(const std::string& bonustype)
 {
@@ -712,6 +764,14 @@ Player::set_bonus(BonusType type, bool animate)
       std::string action = (dir==LEFT)?"left":"right";
       Sector::current()->add_object(new SpriteParticle("images/objects/particles/firetux-helmet.sprite", action, ppos, ANCHOR_TOP, pspeed, paccel, LAYER_OBJECTS-1));
     }
+    if ((player_status->bonus == ICE_BONUS) && (animate)) {
+      // visually lose cap
+      Vector ppos = Vector((bbox.p1.x + bbox.p2.x) / 2, bbox.p1.y);
+      Vector pspeed = Vector(((dir==LEFT) ? +100 : -100), -300);
+      Vector paccel = Vector(0, 1000);
+      std::string action = (dir==LEFT)?"left":"right";
+      Sector::current()->add_object(new SpriteParticle("images/objects/particles/icetux-cap.sprite", action, ppos, ANCHOR_TOP, pspeed, paccel, LAYER_OBJECTS-1));
+    }
     player_status->max_fire_bullets = 0;
     player_status->max_ice_bullets = 0;
   }
@@ -750,6 +810,14 @@ Player::draw(DrawingContext& context)
   if(!visible)
     return;
 
+  // if Tux is above camera, draw little "air arrow" to show where he is x-wise
+  if (Sector::current() && Sector::current()->camera && (get_bbox().p2.y + 16 < Sector::current()->camera->get_translation().y)) {
+    float px = get_pos().x + (get_bbox().p2.x - get_bbox().p1.x - airarrow.get()->get_width()) / 2;
+    float py = Sector::current()->camera->get_translation().y;
+    py += std::min(((py - (get_bbox().p2.y + 16)) / 4), 16.0f);
+    context.draw_surface(airarrow.get(), Vector(px, py), LAYER_HUD - 1);
+  }
+
   TuxBodyParts* tux_body;
 
   if (player_status->bonus == GROWUP_BONUS)
@@ -933,14 +1001,9 @@ Player::collision(GameObject& other, const CollisionHit& hit)
     return FORCE_MOVE;
   }
 
-  // if we hit something from the side that is portable, the ACTION button is pressed and we are not already holding anything: grab it
-  Portable* portable = dynamic_cast<Portable*> (&other);
-  if ((hit.left || hit.right) && (portable && portable->is_portable()) && controller->hold(Controller::ACTION) && (!grabbed_object)) {
-    grabbed_object = portable;
-    grabbed_object->grab(*this, get_pos(), dir);
-    return CONTINUE;
+  if(hit.left || hit.right) {
+    try_grab(); //grab objects right now, in update it will be too late
   }
-
 #ifdef DEBUG
   assert(dynamic_cast<MovingObject*> (&other) != NULL);
 #endif