Fixed problems with Rockets and Cannons sometimes reversing direction on
[supertux.git] / src / badguy / snail.cpp
1 //  $Id$
2 //
3 //  SuperTux - Badguy "Snail"
4 //  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
5 //
6 //  This program is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU General Public License
8 //  as published by the Free Software Foundation; either version 2
9 //  of the License, or (at your option) any later version.
10 //
11 //  This program is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //  GNU General Public License for more details.
15 //
16 //  You should have received a copy of the GNU General Public License
17 //  along with this program; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19
20 #include <config.h>
21
22 #include "snail.hpp"
23 #include "object/block.hpp"
24
25 namespace {
26   const float WALKSPEED = 80;
27   const float KICKSPEED = 500;
28   const int MAXSQUISHES = 10;
29   const float KICKSPEED_Y = -500; /**< y-velocity gained when kicked */
30 }
31
32 Snail::Snail(const lisp::Lisp& reader)
33   : BadGuy(reader, "images/creatures/snail/snail.sprite"), state(STATE_NORMAL), squishcount(0)
34 {
35   sound_manager->preload("sounds/iceblock_bump.wav");
36   sound_manager->preload("sounds/stomp.wav");
37   sound_manager->preload("sounds/kick.wav");
38 }
39
40 Snail::Snail(const Vector& pos, Direction d)
41   : BadGuy(pos, d, "images/creatures/snail/snail.sprite"), state(STATE_NORMAL), squishcount(0)
42 {
43   sound_manager->preload("sounds/iceblock_bump.wav");
44   sound_manager->preload("sounds/stomp.wav");
45   sound_manager->preload("sounds/kick.wav");
46 }
47
48 void
49 Snail::write(lisp::Writer& writer)
50 {
51   writer.start_list("snail");
52
53   writer.write_float("x", start_position.x);
54   writer.write_float("y", start_position.y);
55
56   writer.end_list("snail");
57 }
58
59 void
60 Snail::activate()
61 {
62   be_normal();
63 }
64
65 void
66 Snail::be_normal()
67 {
68   state = STATE_NORMAL;
69   sprite->set_action(dir == LEFT ? "left" : "right");
70
71   physic.set_velocity_x(dir == LEFT ? -WALKSPEED : WALKSPEED);
72 }
73
74 void
75 Snail::be_flat()
76 {
77   state = STATE_FLAT;
78   sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
79   sprite->set_fps(64);
80   
81   physic.set_velocity_x(0);
82   physic.set_velocity_y(0); 
83   
84   flat_timer.start(4);
85 }
86
87 void
88 Snail::be_kicked()
89 {
90   state = STATE_KICKED_DELAY;
91   sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
92   sprite->set_fps(64);
93
94   physic.set_velocity_x(0);
95   physic.set_velocity_y(0); 
96  
97   // start a timer to delay addition of upward movement until we are (hopefully) out from under the player
98   kicked_delay_timer.start(0.05);
99 }
100
101
102 void
103 Snail::active_update(float elapsed_time)
104 {
105   switch (state) {
106
107     case STATE_NORMAL:
108       if (might_fall(601)) {
109         dir = (dir == LEFT ? RIGHT : LEFT);
110         sprite->set_action(dir == LEFT ? "left" : "right");
111         physic.set_velocity_x(-physic.get_velocity_x());
112       }
113       break;
114
115     case STATE_FLAT:
116       if (flat_timer.started()) {
117         sprite->set_fps(64 - 15 * flat_timer.get_timegone());
118       }
119       if (flat_timer.check()) {
120         be_normal();
121       }
122       break;
123
124     case STATE_KICKED_DELAY:
125       if (kicked_delay_timer.check()) {
126         physic.set_velocity_x(dir == LEFT ? -KICKSPEED : KICKSPEED);
127         physic.set_velocity_y(KICKSPEED_Y);
128         state = STATE_KICKED;
129       }
130       break;
131
132     case STATE_KICKED:
133       physic.set_velocity_x(physic.get_velocity_x() * pow(0.99, elapsed_time/0.02));
134       if (fabsf(physic.get_velocity_x()) < WALKSPEED) be_normal();
135       break;
136
137   }
138   BadGuy::active_update(elapsed_time);
139 }
140
141 HitResponse
142 Snail::collision_solid(GameObject& object, const CollisionHit& hit)
143 {
144   if(fabsf(hit.normal.y) > .5) { // floor or roof
145     physic.set_velocity_y(0);
146
147     switch (state) {
148       case STATE_NORMAL:
149       case STATE_FLAT:
150       case STATE_KICKED_DELAY:
151         break;
152       case STATE_KICKED:
153         break;
154     }
155
156     return CONTINUE;
157   }
158   // hit left or right
159   switch(state) {
160     
161     case STATE_NORMAL:
162       dir = dir == LEFT ? RIGHT : LEFT;
163       sprite->set_action(dir == LEFT ? "left" : "right");
164       physic.set_velocity_x(-physic.get_velocity_x());       
165       break;
166
167     case STATE_FLAT:
168     case STATE_KICKED_DELAY:
169       physic.set_velocity_x(0);
170       break;
171
172     case STATE_KICKED: {
173       sound_manager->play("sounds/iceblock_bump.wav", get_pos());
174      
175       // open bonusblocks, crash bricks
176       BonusBlock* bonusblock = dynamic_cast<BonusBlock*> (&object);
177       if(bonusblock) {
178         bonusblock->try_open();
179       }
180       Brick* brick = dynamic_cast<Brick*> (&object);
181       if(brick) {
182         brick->try_break();
183       }
184       
185       dir = (dir == LEFT) ? RIGHT : LEFT;
186       sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
187
188       physic.set_velocity_x(-physic.get_velocity_x()*0.75);
189       if (fabsf(physic.get_velocity_x()) < WALKSPEED) be_normal();
190       break;
191
192     }
193   }
194
195   return CONTINUE;
196 }
197
198 HitResponse
199 Snail::collision_badguy(BadGuy& badguy, const CollisionHit& hit)
200 {
201   switch(state) {
202     case STATE_NORMAL:
203       if(fabsf(hit.normal.x) > .5) {
204         dir = (dir == LEFT) ? RIGHT : LEFT;
205         sprite->set_action(dir == LEFT ? "left" : "right");
206         physic.set_velocity_x(-physic.get_velocity_x());               
207       }
208       return CONTINUE;
209     case STATE_FLAT:
210     case STATE_KICKED_DELAY:
211       return FORCE_MOVE;
212     case STATE_KICKED:
213       badguy.kill_fall();
214       return FORCE_MOVE;
215     default:
216       assert(false);
217   }
218
219   return ABORT_MOVE;
220 }
221
222 bool
223 Snail::collision_squished(Player& player)
224 {
225   switch(state) {
226
227     case STATE_KICKED:
228     case STATE_NORMAL:
229       squishcount++;
230       if(squishcount >= MAXSQUISHES) {
231         kill_fall();
232         return true;
233       }
234
235       sound_manager->play("sounds/stomp.wav", get_pos());
236       be_flat();
237       break;
238       
239     case STATE_FLAT:
240       sound_manager->play("sounds/kick.wav", get_pos());
241
242       if(player.get_pos().x < get_pos().x) {
243         dir = RIGHT;
244       } else {
245         dir = LEFT;
246       }
247       be_kicked();
248       break;
249
250     case STATE_KICKED_DELAY:
251       break;
252       
253   }
254
255   player.bounce(*this);
256   return true;
257 }
258
259 IMPLEMENT_FACTORY(Snail, "snail")