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