Unified Messaging Subsystem
[supertux.git] / src / object / block.cpp
1 //  $Id$
2 // 
3 //  SuperTux
4 //  Copyright (C) 2005 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
19 //  02111-1307, USA.
20 #include <config.h>
21
22 #include "block.hpp"
23 #include "msg.hpp"
24
25 #include <stdexcept>
26
27 #include "resources.hpp"
28 #include "player.hpp"
29 #include "sector.hpp"
30 #include "sprite/sprite.hpp"
31 #include "sprite/sprite_manager.hpp"
32 #include "video/drawing_context.hpp"
33 #include "lisp/lisp.hpp"
34 #include "gameobjs.hpp"
35 #include "specialriser.hpp"
36 #include "growup.hpp"
37 #include "flower.hpp"
38 #include "oneup.hpp"
39 #include "star.hpp"
40 #include "player_status.hpp"
41 #include "badguy/badguy.hpp"
42 #include "coin.hpp"
43 #include "object_factory.hpp"
44 #include "lisp/list_iterator.hpp"
45 #include "object_factory.hpp"
46
47 static const float BOUNCY_BRICK_MAX_OFFSET=8;
48 static const float BOUNCY_BRICK_SPEED=90;
49 static const float EPSILON = .0001;
50
51 Block::Block(Sprite* newsprite)
52   : sprite(newsprite), bouncing(false), bounce_dir(0), bounce_offset(0)
53 {
54   bbox.set_size(32, 32.1);
55   set_group(COLGROUP_STATIC);
56   flags |= FLAG_SOLID;
57 }
58
59 Block::~Block()
60 {
61   delete sprite;
62 }
63
64 HitResponse
65 Block::collision(GameObject& other, const CollisionHit& hitdata)
66 {
67   Player* player = dynamic_cast<Player*> (&other);
68   if(player) {
69     // collided from below?
70     if(hitdata.normal.x == 0 && hitdata.normal.y < 0) {
71       hit(*player);
72     }
73   }
74
75   if(bouncing) {
76     BadGuy* badguy = dynamic_cast<BadGuy*> (&other);
77     if(badguy) {
78       badguy->kill_fall();
79     }
80     Coin* coin = dynamic_cast<Coin*> (&other);
81     if(coin) {
82       coin->collect();
83     }
84   }
85
86   return FORCE_MOVE;
87 }
88
89 void
90 Block::update(float elapsed_time)
91 {
92   if(!bouncing)
93     return;
94   
95   float offset = original_y - get_pos().y;
96   if(offset > BOUNCY_BRICK_MAX_OFFSET) {
97     bounce_dir = BOUNCY_BRICK_SPEED;
98     movement = Vector(0, bounce_dir * elapsed_time);
99   } else if(offset < BOUNCY_BRICK_SPEED * elapsed_time && bounce_dir > 0) {
100     movement = Vector(0, offset);
101     bounce_dir = 0;
102     bouncing = false;
103   } else {
104     movement = Vector(0, bounce_dir * elapsed_time);
105   }
106 }
107
108 void
109 Block::draw(DrawingContext& context)
110 {
111   sprite->draw(context, get_pos(), LAYER_OBJECTS+1);
112 }
113
114 void
115 Block::start_bounce()
116 {
117   original_y = bbox.p1.y;
118   bouncing = true;
119   bounce_dir = -BOUNCY_BRICK_SPEED;
120   bounce_offset = 0;
121 }
122
123 //---------------------------------------------------------------------------
124
125 BonusBlock::BonusBlock(const Vector& pos, int data)
126   : Block(sprite_manager->create("images/objects/bonus_block/bonusblock.sprite")), object(0)
127 {
128   bbox.set_pos(pos);
129   sprite->set_action("normal");
130   switch(data) {
131     case 1: contents = CONTENT_COIN; break;
132     case 2: contents = CONTENT_FIREGROW; break;
133     case 3: contents = CONTENT_STAR; break;
134     case 4: contents = CONTENT_1UP; break;
135     case 5: contents = CONTENT_ICEGROW; break;
136     default:
137       msg_warning("Invalid box contents");
138       contents = CONTENT_COIN;
139       break;
140   }          
141 }
142
143 BonusBlock::BonusBlock(const lisp::Lisp& lisp)
144   : Block(sprite_manager->create("images/objects/bonus_block/bonusblock.sprite"))
145 {
146   Vector pos;
147
148   contents = CONTENT_COIN;
149   lisp::ListIterator iter(&lisp);
150   while(iter.next()) {
151     const std::string& token = iter.item();
152     if(token == "x") {
153       iter.value()->get(pos.x);
154     } else if(token == "y") {
155       iter.value()->get(pos.y);
156     } else if(token == "contents") {
157       std::string contentstring;
158       iter.value()->get(contentstring);
159       if(contentstring == "coin") {
160         contents = CONTENT_COIN;
161       } else if(contentstring == "firegrow") {
162         contents = CONTENT_FIREGROW;
163       } else if(contentstring == "icegrow") {
164         contents = CONTENT_ICEGROW;
165       } else if(contentstring == "star") {
166         contents = CONTENT_STAR;
167       } else if(contentstring == "1up") {
168         contents = CONTENT_1UP;
169       } else if(contentstring == "custom") {
170         contents = CONTENT_CUSTOM;
171       } else {
172         msg_warning("Invalid box contents '" << contentstring << "'");
173       }
174     } else {
175       if(contents == CONTENT_CUSTOM) {
176         GameObject* game_object = create_object(token, *(iter.lisp()));
177         object = dynamic_cast<MovingObject*> (game_object);
178         if(object == 0)
179           throw std::runtime_error(
180             "Only MovingObjects are allowed inside BonusBlocks");
181       } else {
182         msg_warning("Invalid element '" << token << "' in bonusblock");
183       }
184     }  
185   }
186
187   if(contents == CONTENT_CUSTOM && object == 0)
188     throw std::runtime_error("Need to specify content object for custom block");
189   
190   bbox.set_pos(pos);
191 }
192
193 BonusBlock::~BonusBlock()
194 {
195   delete object;
196 }
197
198 void
199 BonusBlock::hit(Player& )
200 {
201   try_open();
202 }
203
204 void
205 BonusBlock::try_open()
206 {
207   if(sprite->get_action_name() == "empty") {
208     sound_manager->play("sounds/brick.wav");
209     return;
210   }
211   
212   Sector* sector = Sector::current();
213   Player& player = *(sector->player);
214   switch(contents) {
215     case CONTENT_COIN:
216       Sector::current()->add_object(new BouncyCoin(get_pos()));
217       player.get_status()->incCoins();
218       break;
219
220     case CONTENT_FIREGROW:
221       if(player.get_status()->bonus == NO_BONUS) {
222         SpecialRiser* riser = new SpecialRiser(get_pos(), new GrowUp());
223         sector->add_object(riser);
224       } else {
225         SpecialRiser* riser = new SpecialRiser(
226             get_pos(), new Flower(Flower::FIREFLOWER));
227         sector->add_object(riser);
228       }
229       sound_manager->play("sounds/upgrade.wav");
230       break;
231
232     case CONTENT_ICEGROW:
233       if(player.get_status()->bonus == NO_BONUS) {
234         SpecialRiser* riser = new SpecialRiser(get_pos(), new GrowUp());
235         sector->add_object(riser);                                            
236       } else {
237         SpecialRiser* riser = new SpecialRiser(
238             get_pos(), new Flower(Flower::ICEFLOWER));
239         sector->add_object(riser);
240       }      
241       sound_manager->play("sounds/upgrade.wav");
242       break;
243
244     case CONTENT_STAR:
245       sector->add_object(new Star(get_pos() + Vector(0, -32)));
246       break;
247
248     case CONTENT_1UP:
249       sector->add_object(new OneUp(get_pos()));
250       break;
251
252     case CONTENT_CUSTOM:
253       SpecialRiser* riser = new SpecialRiser(get_pos(), object);
254       object = 0;
255       sector->add_object(riser);
256       sound_manager->play("sounds/upgrade.wav");
257       break;
258
259     //default:
260       //assert(false);
261   }
262
263   start_bounce();
264   sprite->set_action("empty");
265 }
266
267 IMPLEMENT_FACTORY(BonusBlock, "bonusblock");
268
269 //---------------------------------------------------------------------------
270
271 Brick::Brick(const Vector& pos, int data)
272   : Block(sprite_manager->create("images/objects/bonus_block/brick.sprite")), breakable(false),
273     coin_counter(0)
274 {
275   bbox.set_pos(pos);
276   if(data == 1)
277     coin_counter = 5;
278   else
279     breakable = true;
280 }
281
282 void
283 Brick::hit(Player& )
284 {
285   if(sprite->get_action_name() == "empty")
286     return;
287   
288   try_break(true);
289 }
290
291 void
292 Brick::try_break(bool playerhit)
293 {
294   if(sprite->get_action_name() == "empty")
295     return;
296   
297   sound_manager->play("sounds/brick.wav");
298   Sector* sector = Sector::current();
299   Player& player = *(sector->player);
300   if(coin_counter > 0) {
301     sector->add_object(new BouncyCoin(get_pos()));
302     coin_counter--;
303     player.get_status()->incCoins();
304     if(coin_counter == 0)
305       sprite->set_action("empty");
306     start_bounce();
307   } else if(breakable) {
308     if(playerhit && !player.is_big()) {
309       start_bounce();
310       return;
311     }
312
313     sector->add_object(
314         new BrokenBrick(new Sprite(*sprite), get_pos(), Vector(-100, -400)));
315     sector->add_object(
316         new BrokenBrick(new Sprite(*sprite), get_pos() + Vector(0, 16),
317           Vector(-150, -300)));
318     sector->add_object(
319         new BrokenBrick(new Sprite(*sprite), get_pos() + Vector(16, 0),
320           Vector(100, -400)));
321     sector->add_object(
322         new BrokenBrick(new Sprite(*sprite), get_pos() + Vector(16, 16),
323           Vector(150, -300)));
324     remove_me();
325   }
326 }
327
328 //IMPLEMENT_FACTORY(Brick, "brick");
329