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