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