Made code -Wshadow clean, missed a bunch of issues in the last commit
[supertux.git] / src / object / bonus_block.cpp
1 //  SuperTux
2 //  Copyright (C) 2009 Ingo Ruhnke <grumbel@gmail.com>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 #include "object/bonus_block.hpp"
18
19 #include "audio/sound_manager.hpp"
20 #include "badguy/badguy.hpp"
21 #include "lisp/list_iterator.hpp"
22 #include "object/flower.hpp"
23 #include "object/bouncy_coin.hpp"
24 #include "object/coin_explode.hpp"
25 #include "object/coin_rain.hpp"
26 #include "object/growup.hpp"
27 #include "object/oneup.hpp"
28 #include "object/player.hpp"
29 #include "object/portable.hpp"
30 #include "object/powerup.hpp"
31 #include "object/specialriser.hpp"
32 #include "object/star.hpp"
33 #include "object/trampoline.hpp"
34 #include "sprite/sprite_manager.hpp"
35 #include "supertux/constants.hpp"
36 #include "supertux/level.hpp"
37 #include "supertux/object_factory.hpp"
38 #include "supertux/sector.hpp"
39
40 #include <stdexcept>
41
42 BonusBlock::BonusBlock(const Vector& pos, int data) :
43   Block(sprite_manager->create("images/objects/bonus_block/bonusblock.sprite")),
44   contents(),
45   object(0),
46   hit_counter(1),
47   sprite_name(),
48   script(),
49   lightsprite()
50 {
51   bbox.set_pos(pos);
52   sprite->set_action("normal");
53   switch(data) {
54     case 1: contents = CONTENT_COIN; break;
55     case 2: contents = CONTENT_FIREGROW; break;
56     case 3: contents = CONTENT_STAR; break;
57     case 4: contents = CONTENT_1UP; break;
58     case 5: contents = CONTENT_ICEGROW; break;
59     case 6: contents = CONTENT_LIGHT;
60       sound_manager->preload("sounds/switch.ogg");
61       lightsprite=Surface::create("/images/objects/lightmap_light/bonusblock_light.png");
62       break;
63     case 7: contents = CONTENT_TRAMPOLINE;
64       //object = new Trampoline(get_pos(), false); //needed if this is to be moved to custom
65       break;
66     case 8: contents = CONTENT_CUSTOM;
67       object = new Trampoline(get_pos(), true);
68       break;
69     case 9: contents = CONTENT_CUSTOM;
70       object = new Rock(get_pos(), "images/objects/rock/rock.sprite");
71       break;
72     case 10: contents = CONTENT_RAIN; break;
73     case 11: contents = CONTENT_EXPLODE; break;
74     case 12: contents = CONTENT_CUSTOM;
75       object = new PowerUp(get_pos(), "images/powerups/potions/red-potion.sprite");
76       break;
77     default:
78       log_warning << "Invalid box contents" << std::endl;
79       contents = CONTENT_COIN;
80       break;
81   }
82 }
83
84 BonusBlock::BonusBlock(const Reader& lisp) :
85   Block(sprite_manager->create("images/objects/bonus_block/bonusblock.sprite")),
86   contents(),
87   object(0),
88   hit_counter(1),
89   sprite_name(),
90   script(),
91   lightsprite()
92 {
93   Vector pos;
94
95   contents = CONTENT_COIN;
96   lisp::ListIterator iter(&lisp);
97   while(iter.next()) {
98     const std::string& token = iter.item();
99     if(token == "x") {
100       iter.value()->get(pos.x);
101     } else if(token == "y") {
102       iter.value()->get(pos.y);
103     } else if(token == "sprite") {
104       iter.value()->get(sprite_name);
105       sprite = sprite_manager->create(sprite_name);
106     } else if(token == "count") {
107       iter.value()->get(hit_counter);
108     } else if(token == "script") {
109       iter.value()->get(script);
110     } else if(token == "contents") {
111       std::string contentstring;
112       iter.value()->get(contentstring);
113       if(contentstring == "coin") {
114         contents = CONTENT_COIN;
115       } else if(contentstring == "firegrow") {
116         contents = CONTENT_FIREGROW;
117       } else if(contentstring == "icegrow") {
118         contents = CONTENT_ICEGROW;
119       } else if(contentstring == "star") {
120         contents = CONTENT_STAR;
121       } else if(contentstring == "1up") {
122         contents = CONTENT_1UP;
123       } else if(contentstring == "custom") {
124         contents = CONTENT_CUSTOM;
125       } else if(contentstring == "script") { // use when bonusblock is to contain ONLY a script
126         contents = CONTENT_SCRIPT;
127       } else if(contentstring == "light") {
128         contents = CONTENT_LIGHT;
129         sound_manager->preload("sounds/switch.ogg");
130       } else if(contentstring == "trampoline") {
131         contents = CONTENT_TRAMPOLINE;
132       } else if(contentstring == "rain") {
133         contents = CONTENT_RAIN;
134       } else if(contentstring == "explode") {
135         contents = CONTENT_EXPLODE;
136       } else {
137         log_warning << "Invalid box contents '" << contentstring << "'" << std::endl;
138       }
139     } else {
140       if(contents == CONTENT_CUSTOM) {
141         GameObject* game_object = ObjectFactory::instance().create(token, *(iter.lisp()));
142         object = dynamic_cast<MovingObject*> (game_object);
143         if(object == 0)
144           throw std::runtime_error(
145             "Only MovingObjects are allowed inside BonusBlocks");
146       } else {
147         log_warning << "Invalid element '" << token << "' in bonusblock" << std::endl;
148       }
149     }
150   }
151
152   if(contents == CONTENT_CUSTOM && object == 0)
153     throw std::runtime_error("Need to specify content object for custom block");
154   if(contents == CONTENT_LIGHT)
155     lightsprite = Surface::create("/images/objects/lightmap_light/bonusblock_light.png");
156
157   bbox.set_pos(pos);
158 }
159
160 BonusBlock::~BonusBlock()
161 {
162   delete object;
163 }
164
165 void
166 BonusBlock::hit(Player & player)
167 {
168   try_open(&player);
169 }
170
171 HitResponse
172 BonusBlock::collision(GameObject& other, const CollisionHit& hit_){
173
174   Player* player = dynamic_cast<Player*> (&other);
175   if (player) {
176     if (player->does_buttjump)
177       try_drop(player);
178   }
179
180   BadGuy* badguy = dynamic_cast<BadGuy*> (&other);
181   if(badguy) {
182     // hit contains no information for collisions with blocks.
183     // Badguy's bottom has to be below the top of the block
184     // SHIFT_DELTA is required to slide over one tile gaps.
185     if( badguy->can_break() && ( badguy->get_bbox().get_bottom() > get_bbox().get_top() + SHIFT_DELTA ) ){
186       try_open(player);
187     }
188   }
189   Portable* portable = dynamic_cast<Portable*> (&other);
190   if(portable) {
191     MovingObject* moving = dynamic_cast<MovingObject*> (&other);
192     if(moving->get_bbox().get_top() > get_bbox().get_bottom() - SHIFT_DELTA) {
193       try_open(player);
194     }
195   }
196   return Block::collision(other, hit_);
197 }
198
199 void
200 BonusBlock::try_open(Player *player)
201 {
202   if(sprite->get_action() == "empty") {
203     sound_manager->play("sounds/brick.wav");
204     return;
205   }
206
207   Sector* sector = Sector::current();
208   assert(sector);
209
210   if (player == NULL)
211     player = sector->player;
212
213   if (player == NULL)
214     return;
215
216   Direction direction = (player->get_bbox().get_middle().x > get_bbox().get_middle().x) ? LEFT : RIGHT;
217
218   switch(contents) {
219     case CONTENT_COIN:
220     {
221       Sector::current()->add_object(new BouncyCoin(get_pos(), true));
222       player->get_status()->add_coins(1);
223       if (hit_counter != 0)
224         Sector::current()->get_level()->stats.coins++;
225       break;
226     }
227
228     case CONTENT_FIREGROW:
229     {
230       if(player->get_status()->bonus == NO_BONUS) {
231         SpecialRiser* riser = new SpecialRiser(get_pos(), new GrowUp(direction));
232         sector->add_object(riser);
233       } else {
234         SpecialRiser* riser = new SpecialRiser(
235           get_pos(), new Flower(FIRE_BONUS));
236         sector->add_object(riser);
237       }
238       sound_manager->play("sounds/upgrade.wav");
239       break;
240     }
241
242     case CONTENT_ICEGROW:
243     {
244       if(player->get_status()->bonus == NO_BONUS) {
245         SpecialRiser* riser = new SpecialRiser(get_pos(), new GrowUp(direction));
246         sector->add_object(riser);
247       } else {
248         SpecialRiser* riser = new SpecialRiser(
249           get_pos(), new Flower(ICE_BONUS));
250         sector->add_object(riser);
251       }
252       sound_manager->play("sounds/upgrade.wav");
253       break;
254     }
255
256     case CONTENT_STAR:
257     {
258       sector->add_object(new Star(get_pos() + Vector(0, -32), direction));
259       sound_manager->play("sounds/upgrade.wav");
260       break;
261     }
262
263     case CONTENT_1UP:
264     {
265       sector->add_object(new OneUp(get_pos(), direction));
266       sound_manager->play("sounds/upgrade.wav");
267       break;
268     }
269
270     case CONTENT_CUSTOM:
271     {
272       SpecialRiser* riser = new SpecialRiser(get_pos(), object);
273       object = 0;
274       sector->add_object(riser);
275       sound_manager->play("sounds/upgrade.wav");
276       break;
277     }
278
279     case CONTENT_SCRIPT:
280     { break; } // because scripts always run, this prevents default contents from being assumed
281
282     case CONTENT_LIGHT:
283     {
284       if(sprite->get_action() == "on")
285         sprite->set_action("off");
286       else
287         sprite->set_action("on");
288       sound_manager->play("sounds/switch.ogg");
289       break;
290     }
291     case CONTENT_TRAMPOLINE:
292     {
293       SpecialRiser* riser = new SpecialRiser(get_pos(), new Trampoline(get_pos(), false));
294       sector->add_object(riser);
295       sound_manager->play("sounds/upgrade.wav");
296       break;
297     }
298     case CONTENT_RAIN:
299     {
300       hit_counter = 1; // multiple hits of coin rain is not allowed
301       Sector::current()->add_object(new CoinRain(get_pos(), true));
302       sound_manager->play("sounds/upgrade.wav");
303       break;
304     }
305     case CONTENT_EXPLODE:
306     {
307       hit_counter = 1; // multiple hits of coin explode is not allowed
308       Sector::current()->add_object(new CoinExplode(get_pos() + Vector (0, -40)));
309       sound_manager->play("sounds/upgrade.wav");
310       break;
311     }
312   }
313
314   if(script != "") { // scripts always run if defined
315     std::istringstream stream(script);
316     Sector::current()->run_script(stream, "BonusBlockScript");
317   }
318
319   start_bounce(player);
320   if(hit_counter <= 0 || contents == CONTENT_LIGHT){ //use 0 to allow infinite hits
321   }else if(hit_counter == 1){
322     sprite->set_action("empty");
323   }else{
324     hit_counter--;
325   }
326 }
327
328 void
329 BonusBlock::try_drop(Player *player)
330 {
331   if(sprite->get_action() == "empty") {
332     sound_manager->play("sounds/brick.wav");
333     return;
334   }
335
336   Sector* sector = Sector::current();
337   assert(sector);
338
339   // First what's below the bonus block, if solid send it up anyway (excepting doll)
340   Rectf dest_;
341   dest_.p1.x = bbox.get_left() + 1;
342   dest_.p1.y = bbox.get_bottom() + 1;
343   dest_.p2.x = bbox.get_right() - 1;
344   dest_.p2.y = dest_.p1.y + 30;
345   if (!Sector::current()->is_free_of_statics(dest_, this, true) && !(contents == CONTENT_1UP)) {
346     try_open(player);
347     return;
348   }
349
350   if (player == NULL)
351     player = sector->player;
352
353   if (player == NULL)
354     return;
355
356   Direction direction = (player->get_bbox().get_middle().x > get_bbox().get_middle().x) ? LEFT : RIGHT;
357
358   bool countdown = false;
359
360   switch(contents) {
361     case CONTENT_COIN:
362     {
363       try_open(player);
364       break;
365     }
366
367     case CONTENT_FIREGROW:
368     {
369       sector->add_object(new PowerUp(get_pos() + Vector(0, 32), "images/powerups/fireflower/fireflower.sprite"));
370       sound_manager->play("sounds/upgrade.wav");
371       countdown = true;
372       break;
373     }
374
375     case CONTENT_ICEGROW:
376     {
377       sector->add_object(new PowerUp(get_pos() + Vector(0, 32), "images/powerups/iceflower/iceflower.sprite"));
378       sound_manager->play("sounds/upgrade.wav");
379       countdown = true;
380       break;
381     }
382
383     case CONTENT_STAR:
384     {
385       sector->add_object(new Star(get_pos() + Vector(0, 32), direction));
386       sound_manager->play("sounds/upgrade.wav");
387       countdown = true;
388       break;
389     }
390
391     case CONTENT_1UP:
392     {
393       sector->add_object(new OneUp(get_pos(), DOWN));
394       sound_manager->play("sounds/upgrade.wav");
395       countdown = true;
396       break;
397     }
398
399     case CONTENT_CUSTOM:
400     {
401       //NOTE: non-portable trampolines could be moved to CONTENT_CUSTOM, but they should not drop
402       object->set_pos(get_pos() +  Vector(0, 32));
403       sector->add_object(object);
404       object = 0;
405       sound_manager->play("sounds/upgrade.wav");
406       countdown = true;
407       break;
408     }
409
410     case CONTENT_SCRIPT:
411     { break; } // because scripts always run, this prevents default contents from being assumed
412
413     case CONTENT_LIGHT:
414     {
415       try_open(player);
416       break;
417     }
418     case CONTENT_TRAMPOLINE:
419     {
420       try_open(player);
421       break;
422     }
423     case CONTENT_RAIN:
424     {
425       try_open(player);
426       break;
427     }
428     case CONTENT_EXPLODE:
429     {
430       hit_counter = 1; // multiple hits of coin explode is not allowed
431       Sector::current()->add_object(new CoinExplode(get_pos() + Vector (0, 40)));
432       sound_manager->play("sounds/upgrade.wav");
433       countdown = true;
434       break;
435     }
436   }
437
438   if(script != "") { // scripts always run if defined
439     std::istringstream stream(script);
440     Sector::current()->run_script(stream, "powerup-script");
441   }
442
443   if(countdown){ // only decrease hit counter if try_open was not called
444     if(hit_counter == 1){
445       sprite->set_action("empty");
446     }else{
447       hit_counter--;
448     }
449   }
450 }
451
452 void
453 BonusBlock::draw(DrawingContext& context){
454   // do the regular drawing first
455   Block::draw(context);
456   // then Draw the light if on.
457   if(sprite->get_action() == "on") {
458     Vector pos = get_pos() + (bbox.get_size().as_vector() - lightsprite->get_size()) / 2;
459     context.push_target();
460     context.set_target(DrawingContext::LIGHTMAP);
461     context.draw_surface(lightsprite, pos, 10);
462     context.pop_target();
463   }
464 }
465 /* EOF */