7ab283788f3fd7953f160ec3ce7e1f32e4ac6a3a
[supertux.git] / src / badguy / mriceblock.cpp
1 //  $Id$
2 //
3 //  SuperTux
4 //  Copyright (C) 2006 Matthias Braun <matze@braunis.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 "mriceblock.hpp"
23 #include "object/block.hpp"
24
25 namespace {
26   const float KICKSPEED = 500;
27   const int MAXSQUISHES = 10;
28   const float NOKICK_TIME = 0.1f;
29 }
30
31 MrIceBlock::MrIceBlock(const lisp::Lisp& reader)
32   : WalkingBadguy(reader, "images/creatures/mr_iceblock/mr_iceblock.sprite", "left", "right"), ice_state(ICESTATE_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 MrIceBlock::MrIceBlock(const Vector& pos, Direction d)
42   : WalkingBadguy(pos, d, "images/creatures/mr_iceblock/mr_iceblock.sprite", "left", "right"), ice_state(ICESTATE_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 MrIceBlock::write(lisp::Writer& writer)
53 {
54   writer.start_list("mriceblock");
55   WalkingBadguy::write(writer);
56   writer.end_list("mriceblock");
57 }
58
59 void
60 MrIceBlock::initialize()
61 {
62   WalkingBadguy::initialize();
63   set_state(ICESTATE_NORMAL);
64 }
65
66 void
67 MrIceBlock::active_update(float elapsed_time)
68 {
69   if(ice_state == ICESTATE_GRABBED)
70     return;
71
72   if(ice_state == ICESTATE_FLAT && flat_timer.check()) {
73     set_state(ICESTATE_NORMAL);
74   }
75
76   if (ice_state == ICESTATE_NORMAL)
77   {
78     WalkingBadguy::active_update(elapsed_time);
79     return;
80   }
81
82   BadGuy::active_update(elapsed_time);
83 }
84
85 bool
86 MrIceBlock::can_break(){
87     return ice_state == ICESTATE_KICKED;
88 }
89
90 void
91 MrIceBlock::collision_solid(const CollisionHit& hit)
92 {
93   update_on_ground_flag(hit);
94
95   if(hit.top || hit.bottom) { // floor or roof
96     physic.set_velocity_y(0);
97     return;
98   }
99
100   // hit left or right
101   switch(ice_state) {
102     case ICESTATE_NORMAL:
103       WalkingBadguy::collision_solid(hit);
104       break;
105     case ICESTATE_KICKED: {
106       if(hit.right && dir == RIGHT) {
107         dir = LEFT;
108         sound_manager->play("sounds/iceblock_bump.wav", get_pos());
109         physic.set_velocity_x(-KICKSPEED);
110       } else if(hit.left && dir == LEFT) {
111         dir = RIGHT;
112         sound_manager->play("sounds/iceblock_bump.wav", get_pos());
113         physic.set_velocity_x(KICKSPEED);
114       }
115       sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
116       break;
117     }
118     case ICESTATE_FLAT:
119       physic.set_velocity_x(0);
120       break;
121     case ICESTATE_GRABBED:
122       break;
123   }
124 }
125
126 HitResponse
127 MrIceBlock::collision(GameObject& object, const CollisionHit& hit)
128 {
129   if(ice_state == ICESTATE_GRABBED)
130     return FORCE_MOVE;
131
132   return BadGuy::collision(object, hit);
133 }
134
135 HitResponse
136 MrIceBlock::collision_player(Player& player, const CollisionHit& hit)
137 {
138   if(ice_state == ICESTATE_GRABBED)
139     return FORCE_MOVE;
140
141   if(dir == UP) {
142     return FORCE_MOVE;
143   }
144
145   // handle kicks from left or right side
146   if(ice_state == ICESTATE_FLAT && get_state() == STATE_ACTIVE) {
147     if(hit.left) {
148       dir = RIGHT;
149       player.kick();
150       set_state(ICESTATE_KICKED);
151       return FORCE_MOVE;
152     } else if(hit.right) {
153       dir = LEFT;
154       player.kick();
155       set_state(ICESTATE_KICKED);
156       return FORCE_MOVE;
157     }
158   }
159
160   return BadGuy::collision_player(player, hit);
161 }
162
163 HitResponse
164 MrIceBlock::collision_badguy(BadGuy& badguy, const CollisionHit& hit)
165 {
166   switch(ice_state) {
167     case ICESTATE_NORMAL:
168       return WalkingBadguy::collision_badguy(badguy, hit);
169     case ICESTATE_FLAT:
170       return FORCE_MOVE;
171     case ICESTATE_KICKED:
172       badguy.kill_fall();
173       return FORCE_MOVE;
174     default:
175       assert(false);
176   }
177
178   return ABORT_MOVE;
179 }
180
181 bool
182 MrIceBlock::collision_squished(GameObject& object)
183 {
184   switch(ice_state) {
185     case ICESTATE_KICKED:
186     case ICESTATE_NORMAL:
187       {
188         Player* player = dynamic_cast<Player*>(&object);
189         squishcount++;
190         if ((squishcount >= MAXSQUISHES) || (player && player->does_buttjump)) {
191           kill_fall();
192           return true;
193         }
194       }
195
196       set_state(ICESTATE_FLAT);
197       nokick_timer.start(NOKICK_TIME);
198       break;
199     case ICESTATE_FLAT:
200       {
201         MovingObject* movingobject = dynamic_cast<MovingObject*>(&object);
202         if (movingobject && (movingobject->get_pos().x < get_pos().x)) {
203           dir = RIGHT;
204         } else {
205           dir = LEFT;
206         }
207       }
208       if (nokick_timer.check()) set_state(ICESTATE_KICKED);
209       break;
210     case ICESTATE_GRABBED:
211       assert(false);
212       break;
213   }
214
215   Player* player = dynamic_cast<Player*>(&object);
216   if (player) player->bounce(*this);
217   return true;
218 }
219
220 void
221 MrIceBlock::set_state(IceState state)
222 {
223   if(ice_state == state)
224     return;
225
226   switch(state) {
227     case ICESTATE_NORMAL:
228       WalkingBadguy::initialize();
229       break;
230     case ICESTATE_FLAT:
231       if(dir == UP) {
232         physic.set_velocity_y(-KICKSPEED);
233         bbox.set_size(34, 31.8f);
234       } else {
235         sound_manager->play("sounds/stomp.wav", get_pos());
236         physic.set_velocity_x(0);
237         physic.set_velocity_y(0);
238
239         sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
240       }
241       flat_timer.start(4);
242       break;
243     case ICESTATE_KICKED:
244       sound_manager->play("sounds/kick.wav", get_pos());
245
246       physic.set_velocity_x(dir == LEFT ? -KICKSPEED : KICKSPEED);
247       sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
248       // we should slide above 1 block holes now...
249       bbox.set_size(34, 31.8f);
250       break;
251     case ICESTATE_GRABBED:
252       flat_timer.stop();
253       break;
254     default:
255       assert(false);
256   }
257   ice_state = state;
258 }
259
260 void
261 MrIceBlock::grab(MovingObject&, const Vector& pos, Direction dir)
262 {
263   movement = pos - get_pos();
264   this->dir = dir;
265   sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
266   set_state(ICESTATE_GRABBED);
267   set_colgroup_active(COLGROUP_DISABLED);
268 }
269
270 void
271 MrIceBlock::ungrab(MovingObject& , Direction dir)
272 {
273   this->dir = dir;
274   set_state(dir == UP ? ICESTATE_FLAT : ICESTATE_KICKED);
275   set_colgroup_active(COLGROUP_MOVING);
276 }
277
278 bool
279 MrIceBlock::is_portable() const
280 {
281   return ice_state == ICESTATE_FLAT;
282 }
283
284 IMPLEMENT_FACTORY(MrIceBlock, "mriceblock")