//  IceCrusher - A block to stand on, which can drop down to crush the player
 //  Copyright (C) 2008 Christoph Sommer <christoph.sommer@2008.expires.deltadevelopment.de>
+//  Copyright (C) 2010 Florian Forster <supertux at octo.it>
 //
 //  This program is free software: you can redistribute it and/or modify
 //  it under the terms of the GNU General Public License as published by
 #include "supertux/sector.hpp"
 
 namespace {
-const float DROP_SPEED = 500;
-const float RECOVER_SPEED = 200;
+/* Maximum movement speed in pixels per LOGICAL_FPS */
+const float MAX_DROP_SPEED = 10.0;
+const float RECOVER_SPEED = -3.125;
+const float ACTIVATION_DISTANCE = 4.0;
 }
 
 IceCrusher::IceCrusher(const Reader& reader) :
   MovingSprite(reader, "images/creatures/icecrusher/icecrusher.sprite", LAYER_OBJECTS, COLGROUP_STATIC), 
   state(IDLE), 
   start_position(),
-  speed(Vector(0,0))
+  physic()
 {
   start_position = get_bbox().p1;
   set_state(state, true);
   switch(state) {
     case IDLE:
       set_group(COLGROUP_STATIC);
-      speed=Vector(0,0);
+      physic.enable_gravity (false);
       sprite->set_action("idle");
       break;
     case CRUSHING:
       set_group(COLGROUP_MOVING_STATIC);
-      speed=Vector(0, DROP_SPEED);
-      sprite->set_action("idle");
+      physic.reset ();
+      physic.enable_gravity (true);
+      sprite->set_action("crushing");
       break;
     case RECOVERING:
       set_group(COLGROUP_MOVING_STATIC);
-      speed=Vector(0, -RECOVER_SPEED);
-      sprite->set_action("idle");
+      physic.enable_gravity (false);
+      sprite->set_action("recovering");
       break;
     default:
       log_debug << "IceCrusher in invalid state" << std::endl;
 IceCrusher::collision(GameObject& other, const CollisionHit& hit)
 {
   Player* player = dynamic_cast<Player*>(&other);
+
+  /* If the other object is the player, and the collision is at the bottom of
+   * the ice crusher, hurt the player. */
   if (player && hit.bottom) {
     if(player->is_invincible()) {
-      if (state == CRUSHING) set_state(RECOVERING);
+      if (state == CRUSHING)
+        set_state(RECOVERING);
       return ABORT_MOVE;
     }
     player->kill(false);
-    if (state == CRUSHING) set_state(RECOVERING);
+    if (state == CRUSHING)
+      set_state(RECOVERING);
     return FORCE_MOVE;
   }
   BadGuy* badguy = dynamic_cast<BadGuy*>(&other);
 }
     
 void 
-IceCrusher::collision_solid(const CollisionHit& )
+IceCrusher::collision_solid(const CollisionHit& hit)
 {
   switch(state) {
     case IDLE:
       break;
     case CRUSHING:
-      set_state(RECOVERING);
+      if (hit.bottom) {
+        set_state(RECOVERING);
+      }
       break;
     case RECOVERING:
       break;
 {
   switch(state) {
     case IDLE:
-      if (found_victim()) set_state(CRUSHING);
+      movement = Vector (0, 0);
+      if (found_victim())
+        set_state(CRUSHING);
       break;
     case CRUSHING:
+      movement = physic.get_movement (elapsed_time);
+      if (movement.y > MAX_DROP_SPEED)
+        movement.y = MAX_DROP_SPEED;
       break;
     case RECOVERING:
       if (get_bbox().p1.y <= start_position.y+1) {
         set_pos(start_position);
+        movement = Vector (0, 0);
         set_state(IDLE);
       }
+      else {
+        movement = Vector (0, RECOVER_SPEED);
+      }
       break;
     default:
       log_debug << "IceCrusher in invalid state" << std::endl;
       break;
   }
-  movement = speed * elapsed_time;
 }
 
 Player*
   Player* player = this->get_nearest_player();
   if (!player) return false;
 
-  const Rectf& pr = player->get_bbox();
-  const Rectf& br = get_bbox();
-  if ((pr.p2.x > br.p1.x) && (pr.p1.x < br.p2.x) && (pr.p1.y >= br.p2.y)) {
+  const Rectf& player_bbox = player->get_bbox();
+  const Rectf& crusher_bbox = get_bbox();
+  if ((player_bbox.p1.y >= crusher_bbox.p2.y) /* player is below crusher */
+      && (player_bbox.p2.x > (crusher_bbox.p1.x - ACTIVATION_DISTANCE))
+      && (player_bbox.p1.x < (crusher_bbox.p2.x + ACTIVATION_DISTANCE)))
     return true;
-  }
-  return false;
+  else
+    return false;
 }
 
 /* EOF */