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