873428c14d8d2e34e2dc94e61b20a2564ad5e40d
[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 const float PAUSE_TIME = 0.5;
32 }
33
34 IceCrusher::IceCrusher(const Reader& reader) :
35   MovingSprite(reader, "images/creatures/icecrusher/icecrusher.sprite", LAYER_OBJECTS, COLGROUP_STATIC), 
36   state(IDLE), 
37   start_position(),
38   physic(),
39   cooldown_timer(0.0)
40 {
41   start_position = get_bbox().p1;
42   set_state(state, true);
43 }
44
45 /*
46   IceCrusher::IceCrusher(const IceCrusher& other)
47   : MovingSprite(other), 
48   state(other.state), speed(other.speed) 
49   {
50   start_position = get_bbox().p1;
51   set_state(state, true);
52   }
53 */
54 void 
55 IceCrusher::set_state(IceCrusherState state, bool force) 
56 {
57   if ((this->state == state) && (!force)) return;
58   switch(state) {
59     case IDLE:
60       set_group(COLGROUP_STATIC);
61       physic.enable_gravity (false);
62       sprite->set_action("idle");
63       break;
64     case CRUSHING:
65       set_group(COLGROUP_MOVING_STATIC);
66       physic.reset ();
67       physic.enable_gravity (true);
68       sprite->set_action("crushing");
69       break;
70     case RECOVERING:
71       set_group(COLGROUP_MOVING_STATIC);
72       physic.enable_gravity (false);
73       sprite->set_action("recovering");
74       break;
75     default:
76       log_debug << "IceCrusher in invalid state" << std::endl;
77       break;
78   }
79   this->state = state;
80 }
81
82 HitResponse
83 IceCrusher::collision(GameObject& other, const CollisionHit& hit)
84 {
85   Player* player = dynamic_cast<Player*>(&other);
86
87   /* If the other object is the player, and the collision is at the bottom of
88    * the ice crusher, hurt the player. */
89   if (player && hit.bottom) {
90     if(player->is_invincible()) {
91       if (state == CRUSHING)
92         set_state(RECOVERING);
93       return ABORT_MOVE;
94     }
95     player->kill(false);
96     if (state == CRUSHING)
97       set_state(RECOVERING);
98     return FORCE_MOVE;
99   }
100   BadGuy* badguy = dynamic_cast<BadGuy*>(&other);
101   if (badguy) {
102     badguy->kill_fall();
103   }
104   return FORCE_MOVE;
105 }
106     
107 void 
108 IceCrusher::collision_solid(const CollisionHit& hit)
109 {
110   switch(state) {
111     case IDLE:
112       break;
113     case CRUSHING:
114       if (hit.bottom) {
115         cooldown_timer = PAUSE_TIME;
116         set_state(RECOVERING);
117       }
118       break;
119     case RECOVERING:
120       break;
121     default:
122       log_debug << "IceCrusher in invalid state" << std::endl;
123       break;
124   }
125 }
126
127 void
128 IceCrusher::update(float elapsed_time)
129 {
130   if (cooldown_timer >= elapsed_time)
131   {
132     cooldown_timer -= elapsed_time;
133     return;
134   }
135   else if (cooldown_timer != 0.0)
136   {
137     elapsed_time -= cooldown_timer;
138     cooldown_timer = 0.0;
139   }
140
141   switch(state) {
142     case IDLE:
143       movement = Vector (0, 0);
144       if (found_victim())
145         set_state(CRUSHING);
146       break;
147     case CRUSHING:
148       movement = physic.get_movement (elapsed_time);
149       if (movement.y > MAX_DROP_SPEED)
150         movement.y = MAX_DROP_SPEED;
151       break;
152     case RECOVERING:
153       if (get_bbox().p1.y <= start_position.y+1) {
154         set_pos(start_position);
155         movement = Vector (0, 0);
156         cooldown_timer = PAUSE_TIME;
157         set_state(IDLE);
158       }
159       else {
160         movement = Vector (0, RECOVER_SPEED);
161       }
162       break;
163     default:
164       log_debug << "IceCrusher in invalid state" << std::endl;
165       break;
166   }
167 }
168
169 bool
170 IceCrusher::found_victim()
171 {
172   Player* player = Sector::current()->get_nearest_player (this->get_bbox ());
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 */