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