IceCrusher: Use gavitation rather than linear speed when "crushing".
[supertux.git] / src / object / icecrusher.cpp
1 //  IceCrusher - A block to stand on, which can drop down to crush the player
2 //  Copyright (C) 2008 Christoph Sommer <christoph.sommer@2008.expires.deltadevelopment.de>
3 //  Copyright (C) 2010 Florian Forster <supertux at octo.it>
4 //
5 //  This program is free software: you can redistribute it and/or modify
6 //  it under the terms of the GNU General Public License as published by
7 //  the Free Software Foundation, either version 3 of the License, or
8 //  (at your option) any later version.
9 //
10 //  This program is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU General Public License for more details.
14 //
15 //  You should have received a copy of the GNU General Public License
16 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18 #include "object/icecrusher.hpp"
19
20 #include "badguy/badguy.hpp"
21 #include "sprite/sprite.hpp"
22 #include "object/player.hpp"
23 #include "supertux/object_factory.hpp"
24 #include "supertux/sector.hpp"
25
26 namespace {
27 /* Maximum movement speed in pixels per LOGICAL_FPS */
28 const float MAX_DROP_SPEED = 10.0;
29 const float RECOVER_SPEED = -3.125;
30 const float ACTIVATION_DISTANCE = 4.0;
31 }
32
33 IceCrusher::IceCrusher(const Reader& reader) :
34   MovingSprite(reader, "images/creatures/icecrusher/icecrusher.sprite", LAYER_OBJECTS, COLGROUP_STATIC), 
35   state(IDLE), 
36   start_position(),
37   physic()
38 {
39   start_position = get_bbox().p1;
40   set_state(state, true);
41 }
42
43 /*
44   IceCrusher::IceCrusher(const IceCrusher& other)
45   : MovingSprite(other), 
46   state(other.state), speed(other.speed) 
47   {
48   start_position = get_bbox().p1;
49   set_state(state, true);
50   }
51 */
52 void 
53 IceCrusher::set_state(IceCrusherState state, bool force) 
54 {
55   if ((this->state == state) && (!force)) return;
56   switch(state) {
57     case IDLE:
58       set_group(COLGROUP_STATIC);
59       physic.enable_gravity (false);
60       sprite->set_action("idle");
61       break;
62     case CRUSHING:
63       set_group(COLGROUP_MOVING_STATIC);
64       physic.reset ();
65       physic.enable_gravity (true);
66       sprite->set_action("crushing");
67       break;
68     case RECOVERING:
69       set_group(COLGROUP_MOVING_STATIC);
70       physic.enable_gravity (false);
71       sprite->set_action("recovering");
72       break;
73     default:
74       log_debug << "IceCrusher in invalid state" << std::endl;
75       break;
76   }
77   this->state = state;
78 }
79
80 HitResponse
81 IceCrusher::collision(GameObject& other, const CollisionHit& hit)
82 {
83   Player* player = dynamic_cast<Player*>(&other);
84
85   /* If the other object is the player, and the collision is at the bottom of
86    * the ice crusher, hurt the player. */
87   if (player && hit.bottom) {
88     if(player->is_invincible()) {
89       if (state == CRUSHING)
90         set_state(RECOVERING);
91       return ABORT_MOVE;
92     }
93     player->kill(false);
94     if (state == CRUSHING)
95       set_state(RECOVERING);
96     return FORCE_MOVE;
97   }
98   BadGuy* badguy = dynamic_cast<BadGuy*>(&other);
99   if (badguy) {
100     badguy->kill_fall();
101   }
102   return FORCE_MOVE;
103 }
104     
105 void 
106 IceCrusher::collision_solid(const CollisionHit& hit)
107 {
108   switch(state) {
109     case IDLE:
110       break;
111     case CRUSHING:
112       if (hit.bottom) {
113         set_state(RECOVERING);
114       }
115       break;
116     case RECOVERING:
117       break;
118     default:
119       log_debug << "IceCrusher in invalid state" << std::endl;
120       break;
121   }
122 }
123
124 void
125 IceCrusher::update(float elapsed_time)
126 {
127   switch(state) {
128     case IDLE:
129       movement = Vector (0, 0);
130       if (found_victim())
131         set_state(CRUSHING);
132       break;
133     case CRUSHING:
134       movement = physic.get_movement (elapsed_time);
135       if (movement.y > MAX_DROP_SPEED)
136         movement.y = MAX_DROP_SPEED;
137       break;
138     case RECOVERING:
139       if (get_bbox().p1.y <= start_position.y+1) {
140         set_pos(start_position);
141         movement = Vector (0, 0);
142         set_state(IDLE);
143       }
144       else {
145         movement = Vector (0, RECOVER_SPEED);
146       }
147       break;
148     default:
149       log_debug << "IceCrusher in invalid state" << std::endl;
150       break;
151   }
152 }
153
154 Player*
155 IceCrusher::get_nearest_player()
156 {
157   // FIXME: does not really return nearest player
158
159   std::vector<Player*> players = Sector::current()->get_players();
160   for (std::vector<Player*>::iterator playerIter = players.begin(); playerIter != players.end(); ++playerIter) {
161     Player* player = *playerIter;
162     if (player->is_dying() || player->is_dead()) continue;
163     return player;
164   }
165
166   return 0;
167 }
168
169 bool
170 IceCrusher::found_victim()
171 {
172   Player* player = this->get_nearest_player();
173   if (!player) return false;
174
175   const Rectf& player_bbox = player->get_bbox();
176   const Rectf& crusher_bbox = get_bbox();
177   if ((player_bbox.p1.y >= crusher_bbox.p2.y) /* player is below crusher */
178       && (player_bbox.p2.x > (crusher_bbox.p1.x - ACTIVATION_DISTANCE))
179       && (player_bbox.p1.x < (crusher_bbox.p2.x + ACTIVATION_DISTANCE)))
180     return true;
181   else
182     return false;
183 }
184
185 /* EOF */