Butt Jump. As Big Tux, gain lots of speed and press down, then smash bricks, Ice...
[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::activate()
61 {
62   WalkingBadguy::activate();
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         if(++squishcount >= MAXSQUISHES) { kill_fall(); break; }
110         physic.set_velocity_x(-KICKSPEED);
111       } else if(hit.left && dir == LEFT) {
112         dir = RIGHT;
113         sound_manager->play("sounds/iceblock_bump.wav", get_pos());
114         if(++squishcount >= MAXSQUISHES) { kill_fall(); break; }
115         physic.set_velocity_x(KICKSPEED);
116       }
117       sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
118       break;
119     }
120     case ICESTATE_FLAT:
121       physic.set_velocity_x(0);
122       break;
123     case ICESTATE_GRABBED:
124       break;
125   }
126 }
127
128 HitResponse
129 MrIceBlock::collision(GameObject& object, const CollisionHit& hit)
130 {
131   if(ice_state == ICESTATE_GRABBED)
132     return FORCE_MOVE;
133
134   return BadGuy::collision(object, hit);
135 }
136
137 HitResponse
138 MrIceBlock::collision_player(Player& player, const CollisionHit& hit)
139 {
140   if(ice_state == ICESTATE_GRABBED)
141     return FORCE_MOVE;
142
143   if(dir == UP) {
144     return FORCE_MOVE;
145   }
146
147   // handle kicks from left or right side
148   if(ice_state == ICESTATE_FLAT && get_state() == STATE_ACTIVE) {
149     if(hit.left) {
150       dir = RIGHT;
151       player.kick();
152       set_state(ICESTATE_KICKED);
153       return FORCE_MOVE;
154     } else if(hit.right) {
155       dir = LEFT;
156       player.kick();
157       set_state(ICESTATE_KICKED);
158       return FORCE_MOVE;
159     }
160   }
161
162   return BadGuy::collision_player(player, hit);
163 }
164
165 HitResponse
166 MrIceBlock::collision_badguy(BadGuy& badguy, const CollisionHit& hit)
167 {
168   switch(ice_state) {
169     case ICESTATE_NORMAL:
170       return WalkingBadguy::collision_badguy(badguy, hit);
171     case ICESTATE_FLAT:
172       return FORCE_MOVE;
173     case ICESTATE_KICKED:
174       badguy.kill_fall();
175       return FORCE_MOVE;
176     default:
177       assert(false);
178   }
179
180   return ABORT_MOVE;
181 }
182
183 bool
184 MrIceBlock::collision_squished(GameObject& object)
185 {
186   switch(ice_state) {
187     case ICESTATE_KICKED:
188     case ICESTATE_NORMAL:
189       {
190         Player* player = dynamic_cast<Player*>(&object);
191         squishcount++;
192         if ((squishcount >= MAXSQUISHES) || (player && player->butt_jump)) {
193           kill_fall();
194           return true;
195         }
196       }
197
198       set_state(ICESTATE_FLAT);
199       nokick_timer.start(NOKICK_TIME);
200       break;
201     case ICESTATE_FLAT:
202       {
203         MovingObject* movingobject = dynamic_cast<MovingObject*>(&object);
204         if (movingobject && (movingobject->get_pos().x < get_pos().x)) {
205           dir = RIGHT;
206         } else {
207           dir = LEFT;
208         }
209       }
210       if (nokick_timer.check()) set_state(ICESTATE_KICKED);
211       break;
212     case ICESTATE_GRABBED:
213       assert(false);
214       break;
215   }
216
217   Player* player = dynamic_cast<Player*>(&object);
218   if (player) player->bounce(*this);
219   return true;
220 }
221
222 void
223 MrIceBlock::set_state(IceState state)
224 {
225   if(ice_state == state)
226     return;
227
228   switch(state) {
229     case ICESTATE_NORMAL:
230       WalkingBadguy::activate();
231       break;
232     case ICESTATE_FLAT:
233       if(dir == UP) {
234         physic.set_velocity_y(-KICKSPEED);
235         bbox.set_size(34, 31.8f);
236       } else {
237         sound_manager->play("sounds/stomp.wav", get_pos());
238         physic.set_velocity_x(0);
239         physic.set_velocity_y(0);
240
241         sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
242       }
243       flat_timer.start(4);
244       break;
245     case ICESTATE_KICKED:
246       sound_manager->play("sounds/kick.wav", get_pos());
247
248       physic.set_velocity_x(dir == LEFT ? -KICKSPEED : KICKSPEED);
249       sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
250       // we should slide above 1 block holes now...
251       bbox.set_size(34, 31.8f);
252       break;
253     case ICESTATE_GRABBED:
254       flat_timer.stop();
255       break;
256     default:
257       assert(false);
258   }
259   ice_state = state;
260 }
261
262 void
263 MrIceBlock::grab(MovingObject&, const Vector& pos, Direction dir)
264 {
265   movement = pos - get_pos();
266   this->dir = dir;
267   sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
268   set_state(ICESTATE_GRABBED);
269   set_group(COLGROUP_DISABLED);
270 }
271
272 void
273 MrIceBlock::ungrab(MovingObject& , Direction dir)
274 {
275   this->dir = dir;
276   set_state(dir == UP ? ICESTATE_FLAT : ICESTATE_KICKED);
277   set_group(COLGROUP_MOVING);
278 }
279
280 bool
281 MrIceBlock::is_portable() const
282 {
283   return ice_state == ICESTATE_FLAT;
284 }
285
286 IMPLEMENT_FACTORY(MrIceBlock, "mriceblock")