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