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