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