Colors patch from qmax ( + added stat_hdr_color into levelintro.hpp...)
[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       {
187         BadGuy* badguy = dynamic_cast<BadGuy*>(&object);
188         if (badguy) {
189           badguy->kill_fall();
190           break;
191         }
192       }
193
194       // fall through
195     case ICESTATE_NORMAL:
196       {
197         Player* player = dynamic_cast<Player*>(&object);
198         squishcount++;
199         if ((squishcount >= MAXSQUISHES) || (player && player->does_buttjump)) {
200           kill_fall();
201           return true;
202         }
203       }
204
205       set_state(ICESTATE_FLAT);
206       nokick_timer.start(NOKICK_TIME);
207       break;
208     case ICESTATE_FLAT:
209       {
210         MovingObject* movingobject = dynamic_cast<MovingObject*>(&object);
211         if (movingobject && (movingobject->get_pos().x < get_pos().x)) {
212           dir = RIGHT;
213         } else {
214           dir = LEFT;
215         }
216       }
217       if (nokick_timer.check()) set_state(ICESTATE_KICKED);
218       break;
219     case ICESTATE_GRABBED:
220       assert(false);
221       break;
222   }
223
224   Player* player = dynamic_cast<Player*>(&object);
225   if (player) player->bounce(*this);
226   return true;
227 }
228
229 void
230 MrIceBlock::set_state(IceState state)
231 {
232   if(ice_state == state)
233     return;
234
235   switch(state) {
236     case ICESTATE_NORMAL:
237       WalkingBadguy::initialize();
238       break;
239     case ICESTATE_FLAT:
240       if(dir == UP) {
241         physic.set_velocity_y(-KICKSPEED);
242         bbox.set_size(34, 31.8f);
243       } else {
244         sound_manager->play("sounds/stomp.wav", get_pos());
245         physic.set_velocity_x(0);
246         physic.set_velocity_y(0);
247
248         sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
249       }
250       flat_timer.start(4);
251       break;
252     case ICESTATE_KICKED:
253       sound_manager->play("sounds/kick.wav", get_pos());
254
255       physic.set_velocity_x(dir == LEFT ? -KICKSPEED : KICKSPEED);
256       sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
257       // we should slide above 1 block holes now...
258       bbox.set_size(34, 31.8f);
259       break;
260     case ICESTATE_GRABBED:
261       flat_timer.stop();
262       break;
263     default:
264       assert(false);
265   }
266   ice_state = state;
267 }
268
269 void
270 MrIceBlock::grab(MovingObject&, const Vector& pos, Direction dir)
271 {
272   movement = pos - get_pos();
273   this->dir = dir;
274   sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
275   set_state(ICESTATE_GRABBED);
276   set_colgroup_active(COLGROUP_DISABLED);
277 }
278
279 void
280 MrIceBlock::ungrab(MovingObject& , Direction dir)
281 {
282   this->dir = dir;
283   set_state(dir == UP ? ICESTATE_FLAT : ICESTATE_KICKED);
284   set_colgroup_active(COLGROUP_MOVING);
285 }
286
287 bool
288 MrIceBlock::is_portable() const
289 {
290   return ice_state == ICESTATE_FLAT;
291 }
292
293 IMPLEMENT_FACTORY(MrIceBlock, "mriceblock")