Powerup: Iceflower improvements
[supertux.git] / src / badguy / snail.cpp
1 //  SuperTux - Badguy "Snail"
2 //  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 #include "badguy/snail.hpp"
18
19 #include "audio/sound_manager.hpp"
20 #include "object/player.hpp"
21 #include "sprite/sprite.hpp"
22 #include "supertux/object_factory.hpp"
23
24 #include <math.h>
25
26 namespace {
27 const float SNAIL_KICK_SPEED = 500;
28 const int MAX_SNAIL_SQUISHES = 10;
29 const float SNAIL_KICK_SPEED_Y = -500; /**< y-velocity gained when kicked */
30 }
31
32 Snail::Snail(const Reader& reader) :
33   WalkingBadguy(reader, "images/creatures/snail/snail.sprite", "left", "right"), 
34   state(STATE_NORMAL), 
35   kicked_delay_timer(),
36   squishcount(0)
37 {
38   walk_speed = 80;
39   max_drop_height = 600;
40   sound_manager->preload("sounds/iceblock_bump.wav");
41   sound_manager->preload("sounds/stomp.wav");
42   sound_manager->preload("sounds/kick.wav");
43 }
44
45 Snail::Snail(const Vector& pos, Direction d) :
46   WalkingBadguy(pos, d, "images/creatures/snail/snail.sprite", "left", "right"), 
47   state(STATE_NORMAL), 
48   kicked_delay_timer(),
49   squishcount(0)
50 {
51   walk_speed = 80;
52   max_drop_height = 600;
53   sound_manager->preload("sounds/iceblock_bump.wav");
54   sound_manager->preload("sounds/stomp.wav");
55   sound_manager->preload("sounds/kick.wav");
56 }
57
58 void
59 Snail::initialize()
60 {
61   WalkingBadguy::initialize();
62   be_normal();
63 }
64
65 void
66 Snail::be_normal()
67 {
68   if (state == STATE_NORMAL) return;
69
70   state = STATE_NORMAL;
71   WalkingBadguy::initialize();
72 }
73
74 void
75 Snail::be_flat()
76 {
77   state = STATE_FLAT;
78   sprite->set_action(dir == LEFT ? "flat-left" : "flat-right", 1);
79
80   physic.set_velocity_x(0);
81   physic.set_velocity_y(0);
82 }
83
84 void
85 Snail::be_kicked()
86 {
87   state = STATE_KICKED_DELAY;
88   sprite->set_action(dir == LEFT ? "flat-left" : "flat-right", 1);
89
90   physic.set_velocity_x(dir == LEFT ? -SNAIL_KICK_SPEED : SNAIL_KICK_SPEED);
91   physic.set_velocity_y(0);
92
93   // start a timer to delay addition of upward movement until we are (hopefully) out from under the player
94   kicked_delay_timer.start(0.05f);
95 }
96
97 bool
98 Snail::can_break(){
99   return state == STATE_KICKED;
100 }
101
102 void
103 Snail::active_update(float elapsed_time)
104 {
105   if(frozen)
106   {
107     BadGuy::active_update(elapsed_time);
108     return;
109   }
110
111   switch (state) {
112
113     case STATE_NORMAL:
114       WalkingBadguy::active_update(elapsed_time);
115       return;
116
117     case STATE_FLAT:
118       if (sprite->animation_done()) {
119         be_normal();
120       }
121       break;
122
123     case STATE_KICKED_DELAY:
124       if (kicked_delay_timer.check()) {
125         physic.set_velocity_x(dir == LEFT ? -SNAIL_KICK_SPEED : SNAIL_KICK_SPEED);
126         physic.set_velocity_y(SNAIL_KICK_SPEED_Y);
127         state = STATE_KICKED;
128       }
129       break;
130
131     case STATE_KICKED:
132       physic.set_velocity_x(physic.get_velocity_x() * pow(0.99, elapsed_time/0.02));
133       if (sprite->animation_done() || (fabsf(physic.get_velocity_x()) < walk_speed)) be_normal();
134       break;
135
136   }
137
138   BadGuy::active_update(elapsed_time);
139 }
140
141 bool
142 Snail::is_freezable() const
143 {
144   return true;
145 }
146
147 void
148 Snail::collision_solid(const CollisionHit& hit)
149 {
150   if(frozen)
151   {
152     WalkingBadguy::collision_solid(hit);
153     return;
154   }
155
156   switch (state) {
157     case STATE_NORMAL:
158       WalkingBadguy::collision_solid(hit);
159       return;
160     case STATE_KICKED:
161       if(hit.left || hit.right) {
162         sound_manager->play("sounds/iceblock_bump.wav", get_pos());
163
164         if( ( dir == LEFT && hit.left ) || ( dir == RIGHT && hit.right) ){
165           dir = (dir == LEFT) ? RIGHT : LEFT;
166           sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
167
168           physic.set_velocity_x(-physic.get_velocity_x());
169         }
170       }
171       /* fall-through */
172     case STATE_FLAT:
173     case STATE_KICKED_DELAY:
174       if(hit.top || hit.bottom) {
175         physic.set_velocity_y(0);
176       }
177       break;
178   }
179
180   update_on_ground_flag(hit);
181
182 }
183
184 HitResponse
185 Snail::collision_badguy(BadGuy& badguy, const CollisionHit& hit)
186 {
187   if(frozen)
188     return WalkingBadguy::collision_badguy(badguy, hit);
189
190   switch(state) {
191     case STATE_NORMAL:
192       return WalkingBadguy::collision_badguy(badguy, hit);
193     case STATE_FLAT:
194     case STATE_KICKED_DELAY:
195       return FORCE_MOVE;
196     case STATE_KICKED:
197       badguy.kill_fall();
198       return FORCE_MOVE;
199     default:
200       assert(false);
201   }
202
203   return ABORT_MOVE;
204 }
205
206 HitResponse
207 Snail::collision_player(Player& player, const CollisionHit& hit)
208 {
209   if(frozen)
210     return WalkingBadguy::collision_player(player, hit);
211
212   // handle kicks from left or right side
213   if(state == STATE_FLAT && (hit.left || hit.right)) {
214     if(hit.left) {
215       dir = RIGHT;
216     } else if(hit.right) {
217       dir = LEFT;
218     }
219     player.kick();
220     be_kicked();
221     return FORCE_MOVE;
222   }
223
224   return BadGuy::collision_player(player, hit);
225 }
226
227 bool
228 Snail::collision_squished(GameObject& object)
229 {
230   if(frozen)
231     return WalkingBadguy::collision_squished(object);
232
233   Player* player = dynamic_cast<Player*>(&object);
234   if(player && (player->does_buttjump || player->is_invincible())) {
235     kill_fall();
236     player->bounce(*this);
237     return true;
238   }
239
240   switch(state) {
241
242     case STATE_KICKED:
243     case STATE_NORMAL:
244
245       // Can't stomp in midair
246       if(!on_ground())
247         break;
248
249       squishcount++;
250       if (squishcount >= MAX_SNAIL_SQUISHES) {
251         kill_fall();
252         return true;
253       }
254       sound_manager->play("sounds/stomp.wav", get_pos());
255       be_flat();
256       break;
257
258     case STATE_FLAT:
259       sound_manager->play("sounds/kick.wav", get_pos());
260       {
261         MovingObject* movingobject = dynamic_cast<MovingObject*>(&object);
262         if (movingobject && (movingobject->get_pos().x < get_pos().x)) {
263           dir = RIGHT;
264         } else {
265           dir = LEFT;
266         }
267       }
268       be_kicked();
269       break;
270
271     case STATE_KICKED_DELAY:
272       break;
273
274   }
275
276   if (player) player->bounce(*this);
277   return true;
278 }
279
280 /* EOF */