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