Made invincibility sparkle a particle effect
[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   reader.get("direction", direction);
36   set_direction = false;
37   if( direction != "auto" && direction != ""){
38     set_direction = true;
39     initial_direction = str2dir( direction );
40   }
41   sound_manager->preload("sounds/iceblock_bump.wav");
42   sound_manager->preload("sounds/stomp.wav");
43   sound_manager->preload("sounds/kick.wav");
44 }
45
46 Snail::Snail(const Vector& pos, Direction d)
47   : BadGuy(pos, "images/creatures/snail/snail.sprite"), state(STATE_NORMAL), squishcount(0)
48 {
49   set_direction = true;
50   initial_direction = d;
51   sound_manager->preload("sounds/iceblock_bump.wav");
52   sound_manager->preload("sounds/stomp.wav");
53   sound_manager->preload("sounds/kick.wav");
54 }
55
56 void
57 Snail::write(lisp::Writer& writer)
58 {
59   writer.start_list("snail");
60
61   writer.write_string("direction", direction);
62   writer.write_float("x", start_position.x);
63   writer.write_float("y", start_position.y);
64
65   writer.end_list("snail");
66 }
67
68 void
69 Snail::activate()
70 {
71   if (set_direction) {dir = initial_direction;}
72
73   be_normal();
74 }
75
76 void
77 Snail::be_normal()
78 {
79   state = STATE_NORMAL;
80   sprite->set_action(dir == LEFT ? "left" : "right");
81
82   physic.set_velocity_x(dir == LEFT ? -WALKSPEED : WALKSPEED);
83 }
84
85 void
86 Snail::be_flat()
87 {
88   state = STATE_FLAT;
89   sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
90   sprite->set_fps(64);
91   
92   physic.set_velocity_x(0);
93   physic.set_velocity_y(0); 
94   
95   flat_timer.start(4);
96 }
97
98 void
99 Snail::be_kicked()
100 {
101   state = STATE_KICKED_DELAY;
102   sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
103   sprite->set_fps(64);
104
105   physic.set_velocity_x(0);
106   physic.set_velocity_y(0); 
107  
108   // start a timer to delay addition of upward movement until we are (hopefully) out from under the player
109   kicked_delay_timer.start(0.05);
110 }
111
112
113 void
114 Snail::active_update(float elapsed_time)
115 {
116   switch (state) {
117
118     case STATE_NORMAL:
119       if (might_fall(601)) {
120         dir = (dir == LEFT ? RIGHT : LEFT);
121         sprite->set_action(dir == LEFT ? "left" : "right");
122         physic.set_velocity_x(-physic.get_velocity_x());
123       }
124       break;
125
126     case STATE_FLAT:
127       if (flat_timer.started()) {
128         sprite->set_fps(64 - 15 * flat_timer.get_timegone());
129       }
130       if (flat_timer.check()) {
131         be_normal();
132       }
133       break;
134
135     case STATE_KICKED_DELAY:
136       if (kicked_delay_timer.check()) {
137         physic.set_velocity_x(dir == LEFT ? -KICKSPEED : KICKSPEED);
138         physic.set_velocity_y(KICKSPEED_Y);
139         state = STATE_KICKED;
140       }
141       break;
142
143     case STATE_KICKED:
144       physic.set_velocity_x(physic.get_velocity_x() * pow(0.99, elapsed_time/0.02));
145       if (fabsf(physic.get_velocity_x()) < WALKSPEED) be_normal();
146       break;
147
148   }
149   BadGuy::active_update(elapsed_time);
150 }
151
152 HitResponse
153 Snail::collision_solid(GameObject& object, const CollisionHit& hit)
154 {
155   if(fabsf(hit.normal.y) > .5) { // floor or roof
156     physic.set_velocity_y(0);
157
158     switch (state) {
159       case STATE_NORMAL:
160       case STATE_FLAT:
161       case STATE_KICKED_DELAY:
162         break;
163       case STATE_KICKED:
164         break;
165     }
166
167     return CONTINUE;
168   }
169   // hit left or right
170   switch(state) {
171     
172     case STATE_NORMAL:
173       dir = dir == LEFT ? RIGHT : LEFT;
174       sprite->set_action(dir == LEFT ? "left" : "right");
175       physic.set_velocity_x(-physic.get_velocity_x());       
176       break;
177
178     case STATE_FLAT:
179     case STATE_KICKED_DELAY:
180       physic.set_velocity_x(0);
181       break;
182
183     case STATE_KICKED: {
184       sound_manager->play("sounds/iceblock_bump.wav", get_pos());
185      
186       // open bonusblocks, crash bricks
187       BonusBlock* bonusblock = dynamic_cast<BonusBlock*> (&object);
188       if(bonusblock) {
189         bonusblock->try_open();
190       }
191       Brick* brick = dynamic_cast<Brick*> (&object);
192       if(brick) {
193         brick->try_break();
194       }
195       
196       dir = (dir == LEFT) ? RIGHT : LEFT;
197       sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
198
199       physic.set_velocity_x(-physic.get_velocity_x()*0.75);
200       if (fabsf(physic.get_velocity_x()) < WALKSPEED) be_normal();
201       break;
202
203     }
204   }
205
206   return CONTINUE;
207 }
208
209 HitResponse
210 Snail::collision_badguy(BadGuy& badguy, const CollisionHit& hit)
211 {
212   switch(state) {
213     case STATE_NORMAL:
214       if(fabsf(hit.normal.x) > .5) {
215         dir = (dir == LEFT) ? RIGHT : LEFT;
216         sprite->set_action(dir == LEFT ? "left" : "right");
217         physic.set_velocity_x(-physic.get_velocity_x());               
218       }
219       return CONTINUE;
220     case STATE_FLAT:
221     case STATE_KICKED_DELAY:
222       return FORCE_MOVE;
223     case STATE_KICKED:
224       badguy.kill_fall();
225       return FORCE_MOVE;
226     default:
227       assert(false);
228   }
229
230   return ABORT_MOVE;
231 }
232
233 bool
234 Snail::collision_squished(Player& player)
235 {
236   switch(state) {
237
238     case STATE_KICKED:
239     case STATE_NORMAL:
240       squishcount++;
241       if(squishcount >= MAXSQUISHES) {
242         kill_fall();
243         return true;
244       }
245
246       sound_manager->play("sounds/stomp.wav", get_pos());
247       be_flat();
248       break;
249       
250     case STATE_FLAT:
251       sound_manager->play("sounds/kick.wav", get_pos());
252
253       if(player.get_pos().x < get_pos().x) {
254         dir = RIGHT;
255       } else {
256         dir = LEFT;
257       }
258       be_kicked();
259       break;
260
261     case STATE_KICKED_DELAY:
262       break;
263       
264   }
265
266   player.bounce(*this);
267   return true;
268 }
269
270 IMPLEMENT_FACTORY(Snail, "snail")