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