Changed ObjectFactory code so that it works properly when building SuperTux as library
[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 KICKSPEED = 500;
28 const int MAXSQUISHES = 10;
29 const float KICKSPEED_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   flat_timer(),
36   kicked_delay_timer(),
37   squishcount(0)
38 {
39   walk_speed = 80;
40   max_drop_height = 600;
41   sound_manager->preload("sounds/iceblock_bump.wav");
42   sound_manager->preload("sounds/stomp.wav");
43   sound_manager->preload("sounds/kick.wav");
44 }
45
46 Snail::Snail(const Vector& pos, Direction d) :
47   WalkingBadguy(pos, d, "images/creatures/snail/snail.sprite", "left", "right"), 
48   state(STATE_NORMAL), 
49   flat_timer(),
50   kicked_delay_timer(),
51   squishcount(0)
52 {
53   walk_speed = 80;
54   max_drop_height = 600;
55   sound_manager->preload("sounds/iceblock_bump.wav");
56   sound_manager->preload("sounds/stomp.wav");
57   sound_manager->preload("sounds/kick.wav");
58 }
59
60 void
61 Snail::initialize()
62 {
63   WalkingBadguy::initialize();
64   be_normal();
65 }
66
67 void
68 Snail::be_normal()
69 {
70   if (state == STATE_NORMAL) return;
71
72   state = STATE_NORMAL;
73   WalkingBadguy::initialize();
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.05f);
101 }
102
103 bool
104 Snail::can_break(){
105   return state == STATE_KICKED;
106 }
107
108 void
109 Snail::active_update(float elapsed_time)
110 {
111   switch (state) {
112
113     case STATE_NORMAL:
114       WalkingBadguy::active_update(elapsed_time);
115       break;
116
117     case STATE_FLAT:
118       if (flat_timer.started()) {
119         sprite->set_fps(64 - 15 * flat_timer.get_timegone());
120       }
121       if (flat_timer.check()) {
122         be_normal();
123       }
124       BadGuy::active_update(elapsed_time);
125       break;
126
127     case STATE_KICKED_DELAY:
128       if (kicked_delay_timer.check()) {
129         physic.set_velocity_x(dir == LEFT ? -KICKSPEED : KICKSPEED);
130         physic.set_velocity_y(KICKSPEED_Y);
131         state = STATE_KICKED;
132       }
133       BadGuy::active_update(elapsed_time);
134       break;
135
136     case STATE_KICKED:
137       physic.set_velocity_x(physic.get_velocity_x() * pow(0.99, elapsed_time/0.02));
138       if (fabsf(physic.get_velocity_x()) < walk_speed) be_normal();
139       BadGuy::active_update(elapsed_time);
140       break;
141
142   }
143 }
144
145 void
146 Snail::collision_solid(const CollisionHit& hit)
147 {
148   update_on_ground_flag(hit);
149
150   switch (state) {
151     case STATE_NORMAL:
152       WalkingBadguy::collision_solid(hit);
153       break;
154     case STATE_FLAT:
155       if(hit.top || hit.bottom) {
156         physic.set_velocity_y(0);
157       }
158       if(hit.left || hit.right) {
159       }
160       break;
161     case STATE_KICKED_DELAY:
162       if(hit.top || hit.bottom) {
163         physic.set_velocity_y(0);
164       }
165       if(hit.left || hit.right) {
166         physic.set_velocity_x(0);
167       }
168       break;
169     case STATE_KICKED:
170       if(hit.top || hit.bottom) {
171         physic.set_velocity_y(0);
172       }
173       if(hit.left || hit.right) {
174         sound_manager->play("sounds/iceblock_bump.wav", get_pos());
175
176         if( ( dir == LEFT && hit.left ) || ( dir == RIGHT && hit.right) ){
177           dir = (dir == LEFT) ? RIGHT : LEFT;
178           sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
179
180           physic.set_velocity_x(-physic.get_velocity_x()*0.75);
181           if (fabsf(physic.get_velocity_x()) < walk_speed) be_normal();
182         }
183
184       }
185       break;
186   }
187
188 }
189
190 HitResponse
191 Snail::collision_badguy(BadGuy& badguy, const CollisionHit& hit)
192 {
193   switch(state) {
194     case STATE_NORMAL:
195       return WalkingBadguy::collision_badguy(badguy, hit);
196     case STATE_FLAT:
197     case STATE_KICKED_DELAY:
198       return FORCE_MOVE;
199     case STATE_KICKED:
200       badguy.kill_fall();
201       return FORCE_MOVE;
202     default:
203       assert(false);
204   }
205
206   return ABORT_MOVE;
207 }
208
209 bool
210 Snail::collision_squished(GameObject& object)
211 {
212   switch(state) {
213
214     case STATE_KICKED:
215     case STATE_NORMAL:
216     {
217       Player* player = dynamic_cast<Player*>(&object);
218       squishcount++;
219       if ((squishcount >= MAXSQUISHES) || (player && player->does_buttjump)) {
220         kill_fall();
221         return true;
222       }
223     }
224
225     sound_manager->play("sounds/stomp.wav", get_pos());
226     be_flat();
227     break;
228
229     case STATE_FLAT:
230       sound_manager->play("sounds/kick.wav", get_pos());
231       {
232         MovingObject* movingobject = dynamic_cast<MovingObject*>(&object);
233         if (movingobject && (movingobject->get_pos().x < get_pos().x)) {
234           dir = RIGHT;
235         } else {
236           dir = LEFT;
237         }
238       }
239       be_kicked();
240       break;
241
242     case STATE_KICKED_DELAY:
243       break;
244
245   }
246
247   Player* player = dynamic_cast<Player*>(&object);
248   if (player) player->bounce(*this);
249   return true;
250 }
251
252 /* EOF */