Krosh: Add large (4x4) icecrusher sprite and code.
[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 "object/camera.hpp"
24 #include "supertux/object_factory.hpp"
25 #include "supertux/sector.hpp"
26
27 namespace {
28 /* Maximum movement speed in pixels per LOGICAL_FPS */
29 const float MAX_DROP_SPEED = 10.0;
30 const float RECOVER_SPEED_NORMAL = -3.125;
31 const float RECOVER_SPEED_LARGE  = -2.0;
32 const float DROP_ACTIVATION_DISTANCE = 4.0;
33 const float PAUSE_TIME_NORMAL = 0.5;
34 const float PAUSE_TIME_LARGE  = 1.0;
35 }
36
37 IceCrusher::IceCrusher(const Reader& reader) :
38   MovingSprite(reader, "images/creatures/icecrusher/icecrusher.sprite", LAYER_OBJECTS, COLGROUP_STATIC), 
39   state(IDLE), 
40   start_position(),
41   physic(),
42   cooldown_timer(0.0),
43   ic_size(NORMAL)
44 {
45   start_position = get_bbox().p1;
46   set_state(state, true);
47   
48   float sprite_width = sprite->get_width ();
49   log_debug << "My width is " << sprite_width;
50   if (sprite_width >= 128.0)
51     ic_size = LARGE;
52 }
53
54 /*
55   IceCrusher::IceCrusher(const IceCrusher& other)
56   : MovingSprite(other), 
57   state(other.state), speed(other.speed) 
58   {
59   start_position = get_bbox().p1;
60   set_state(state, true);
61   }
62 */
63 void 
64 IceCrusher::set_state(IceCrusherState state, bool force) 
65 {
66   if ((this->state == state) && (!force)) return;
67   switch(state) {
68     case IDLE:
69       set_group(COLGROUP_STATIC);
70       physic.enable_gravity (false);
71       sprite->set_action("idle");
72       break;
73     case CRUSHING:
74       set_group(COLGROUP_MOVING_STATIC);
75       physic.reset ();
76       physic.enable_gravity (true);
77       sprite->set_action("crushing");
78       break;
79     case RECOVERING:
80       set_group(COLGROUP_MOVING_STATIC);
81       physic.enable_gravity (false);
82       sprite->set_action("recovering");
83       break;
84     default:
85       log_debug << "IceCrusher in invalid state" << std::endl;
86       break;
87   }
88   this->state = state;
89 }
90
91 HitResponse
92 IceCrusher::collision(GameObject& other, const CollisionHit& hit)
93 {
94   Player* player = dynamic_cast<Player*>(&other);
95
96   /* If the other object is the player, and the collision is at the bottom of
97    * the ice crusher, hurt the player. */
98   if (player && hit.bottom) {
99     if(player->is_invincible()) {
100       if (state == CRUSHING)
101         set_state(RECOVERING);
102       return ABORT_MOVE;
103     }
104     player->kill(false);
105     if (state == CRUSHING)
106       set_state(RECOVERING);
107     return FORCE_MOVE;
108   }
109   BadGuy* badguy = dynamic_cast<BadGuy*>(&other);
110   if (badguy) {
111     badguy->kill_fall();
112   }
113   return FORCE_MOVE;
114 }
115     
116 void 
117 IceCrusher::collision_solid(const CollisionHit& hit)
118 {
119   switch(state) {
120     case IDLE:
121       break;
122     case CRUSHING:
123       if (hit.bottom) {
124         if (ic_size == LARGE) {
125           cooldown_timer = PAUSE_TIME_LARGE;
126           Sector::current()->camera->shake (/* frequency = */ .125f, /* x = */ 0.0, /* y = */ 16.0);
127         }
128         else {
129           cooldown_timer = PAUSE_TIME_NORMAL;
130           Sector::current()->camera->shake (/* frequency = */ .1f, /* x = */ 0.0, /* y = */ 8.0);
131         }
132         set_state(RECOVERING);
133       }
134       break;
135     case RECOVERING:
136       break;
137     default:
138       log_debug << "IceCrusher in invalid state" << std::endl;
139       break;
140   }
141 }
142
143 void
144 IceCrusher::update(float elapsed_time)
145 {
146   if (cooldown_timer >= elapsed_time)
147   {
148     cooldown_timer -= elapsed_time;
149     return;
150   }
151   else if (cooldown_timer != 0.0)
152   {
153     elapsed_time -= cooldown_timer;
154     cooldown_timer = 0.0;
155   }
156
157   switch(state) {
158     case IDLE:
159       movement = Vector (0, 0);
160       if (found_victim())
161         set_state(CRUSHING);
162       break;
163     case CRUSHING:
164       movement = physic.get_movement (elapsed_time);
165       if (movement.y > MAX_DROP_SPEED)
166         movement.y = MAX_DROP_SPEED;
167       break;
168     case RECOVERING:
169       if (get_bbox().p1.y <= start_position.y+1) {
170         set_pos(start_position);
171         movement = Vector (0, 0);
172         if (ic_size == LARGE)
173           cooldown_timer = PAUSE_TIME_LARGE;
174         else
175           cooldown_timer = PAUSE_TIME_NORMAL;
176         set_state(IDLE);
177       }
178       else {
179         if (ic_size == LARGE)
180           movement = Vector (0, RECOVER_SPEED_LARGE);
181         else
182           movement = Vector (0, RECOVER_SPEED_NORMAL);
183       }
184       break;
185     default:
186       log_debug << "IceCrusher in invalid state" << std::endl;
187       break;
188   }
189 }
190
191 bool
192 IceCrusher::found_victim()
193 {
194   Player* player = Sector::current()->get_nearest_player (this->get_bbox ());
195   if (!player) return false;
196
197   const Rectf& player_bbox = player->get_bbox();
198   const Rectf& crusher_bbox = get_bbox();
199   if ((player_bbox.p1.y >= crusher_bbox.p2.y) /* player is below crusher */
200       && (player_bbox.p2.x > (crusher_bbox.p1.x - DROP_ACTIVATION_DISTANCE))
201       && (player_bbox.p1.x < (crusher_bbox.p2.x + DROP_ACTIVATION_DISTANCE)))
202     return true;
203   else
204     return false;
205 }
206
207 /* EOF */