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