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