cff4b6304fb4f8de9647e9cb6cabb8babdc68ad4
[supertux.git] / src / badguy / snail.cpp
1 //  SuperTux - Badguy "Snail"
2 //  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 #include "badguy/snail.hpp"
18
19 #include "audio/sound_manager.hpp"
20 #include "object/player.hpp"
21 #include "sprite/sprite.hpp"
22 #include "supertux/object_factory.hpp"
23
24 #include <math.h>
25
26 namespace {
27 const float SNAIL_KICK_SPEED = 500;
28 const int MAX_SNAIL_SQUISHES = 10;
29 const float SNAIL_KICK_SPEED_Y = -500; /**< y-velocity gained when kicked */
30 }
31
32 Snail::Snail(const Reader& reader) :
33   WalkingBadguy(reader, "images/creatures/snail/snail.sprite", "left", "right"), 
34   state(STATE_NORMAL), 
35   kicked_delay_timer(),
36   squishcount(0)
37 {
38   walk_speed = 80;
39   max_drop_height = 600;
40   sound_manager->preload("sounds/iceblock_bump.wav");
41   sound_manager->preload("sounds/stomp.wav");
42   sound_manager->preload("sounds/kick.wav");
43 }
44
45 Snail::Snail(const Vector& pos, Direction d) :
46   WalkingBadguy(pos, d, "images/creatures/snail/snail.sprite", "left", "right"), 
47   state(STATE_NORMAL), 
48   kicked_delay_timer(),
49   squishcount(0)
50 {
51   walk_speed = 80;
52   max_drop_height = 600;
53   sound_manager->preload("sounds/iceblock_bump.wav");
54   sound_manager->preload("sounds/stomp.wav");
55   sound_manager->preload("sounds/kick.wav");
56 }
57
58 void
59 Snail::initialize()
60 {
61   WalkingBadguy::initialize();
62   be_normal();
63 }
64
65 void
66 Snail::be_normal()
67 {
68   if (state == STATE_NORMAL) return;
69
70   state = STATE_NORMAL;
71   WalkingBadguy::initialize();
72 }
73
74 void
75 Snail::be_flat()
76 {
77   state = STATE_FLAT;
78   sprite->set_action(dir == LEFT ? "flat-left" : "flat-right", 1);
79
80   physic.set_velocity_x(0);
81   physic.set_velocity_y(0);
82 }
83
84 void
85 Snail::be_kicked()
86 {
87   state = STATE_KICKED_DELAY;
88   sprite->set_action(dir == LEFT ? "flat-left" : "flat-right", 1);
89
90   physic.set_velocity_x(0);
91   physic.set_velocity_y(0);
92
93   // start a timer to delay addition of upward movement until we are (hopefully) out from under the player
94   kicked_delay_timer.start(0.05f);
95 }
96
97 bool
98 Snail::can_break(){
99   return state == STATE_KICKED;
100 }
101
102 void
103 Snail::active_update(float elapsed_time)
104 {
105   switch (state) {
106
107     case STATE_NORMAL:
108       WalkingBadguy::active_update(elapsed_time);
109       return;
110
111     case STATE_FLAT:
112       if (sprite->animation_done()) {
113         be_normal();
114       }
115       break;
116
117     case STATE_KICKED_DELAY:
118       if (kicked_delay_timer.check()) {
119         physic.set_velocity_x(dir == LEFT ? -SNAIL_KICK_SPEED : SNAIL_KICK_SPEED);
120         physic.set_velocity_y(SNAIL_KICK_SPEED_Y);
121         state = STATE_KICKED;
122       }
123       break;
124
125     case STATE_KICKED:
126       physic.set_velocity_x(physic.get_velocity_x() * pow(0.99, elapsed_time/0.02));
127       if (sprite->animation_done() || (fabsf(physic.get_velocity_x()) < walk_speed)) be_normal();
128       break;
129
130   }
131
132   BadGuy::active_update(elapsed_time);
133 }
134
135 void
136 Snail::collision_solid(const CollisionHit& hit)
137 {
138   switch (state) {
139     case STATE_NORMAL:
140       WalkingBadguy::collision_solid(hit);
141       return;
142     case STATE_KICKED:
143       if(hit.left || hit.right) {
144         sound_manager->play("sounds/iceblock_bump.wav", get_pos());
145
146         if( ( dir == LEFT && hit.left ) || ( dir == RIGHT && hit.right) ){
147           dir = (dir == LEFT) ? RIGHT : LEFT;
148           sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
149
150           physic.set_velocity_x(-physic.get_velocity_x());
151         }
152       }
153       /* fall-through */
154     case STATE_FLAT:
155     case STATE_KICKED_DELAY:
156       if(hit.top || hit.bottom) {
157         physic.set_velocity_y(0);
158       }
159       break;
160   }
161
162   update_on_ground_flag(hit);
163
164 }
165
166 HitResponse
167 Snail::collision_badguy(BadGuy& badguy, const CollisionHit& hit)
168 {
169   switch(state) {
170     case STATE_NORMAL:
171       return WalkingBadguy::collision_badguy(badguy, hit);
172     case STATE_FLAT:
173     case STATE_KICKED_DELAY:
174       return FORCE_MOVE;
175     case STATE_KICKED:
176       badguy.kill_fall();
177       return FORCE_MOVE;
178     default:
179       assert(false);
180   }
181
182   return ABORT_MOVE;
183 }
184
185 bool
186 Snail::collision_squished(GameObject& object)
187 {
188   switch(state) {
189
190     case STATE_KICKED:
191     case STATE_NORMAL:
192     {
193       Player* player = dynamic_cast<Player*>(&object);
194       squishcount++;
195       if ((squishcount >= MAX_SNAIL_SQUISHES) || (player && player->does_buttjump)) {
196         kill_fall();
197         return true;
198       }
199     }
200
201     sound_manager->play("sounds/stomp.wav", get_pos());
202     be_flat();
203     break;
204
205     case STATE_FLAT:
206       sound_manager->play("sounds/kick.wav", get_pos());
207       {
208         MovingObject* movingobject = dynamic_cast<MovingObject*>(&object);
209         if (movingobject && (movingobject->get_pos().x < get_pos().x)) {
210           dir = RIGHT;
211         } else {
212           dir = LEFT;
213         }
214       }
215       be_kicked();
216       break;
217
218     case STATE_KICKED_DELAY:
219       break;
220
221   }
222
223   Player* player = dynamic_cast<Player*>(&object);
224   if (player) player->bounce(*this);
225   return true;
226 }
227
228 /* EOF */