- much nicer scrolling
[supertux.git] / src / world.cpp
1 //  $Id$
2 // 
3 //  SuperTux
4 //  Copyright (C) 2000 Bill Kendrick <bill@newbreedsoftware.com>
5 //  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
6 //  Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
7 //
8 //  This program is free software; you can redistribute it and/or
9 //  modify it under the terms of the GNU General Public License
10 //  as published by the Free Software Foundation; either version 2
11 //  of the License, or (at your option) any later version.
12 //
13 //  This program is distributed in the hope that it will be useful,
14 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 //  GNU General Public License for more details.
17 // 
18 //  You should have received a copy of the GNU General Public License
19 //  along with this program; if not, write to the Free Software
20 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21 //  02111-1307, USA.
22
23 #include <iostream>
24 #include <math.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include "globals.h"
28 #include "scene.h"
29 #include "screen.h"
30 #include "defines.h"
31 #include "world.h"
32 #include "level.h"
33 #include "tile.h"
34 #include "resources.h"
35 #include "gameobjs.h"
36
37 Surface* img_distro[4];
38
39 World* World::current_ = 0;
40
41 World::World(const std::string& filename)
42 {
43   // FIXME: Move this to action and draw and everywhere else where the
44   // world calls child functions
45   current_ = this;
46
47   level = new Level(filename);
48   tux.init();
49
50   set_defaults();
51
52   get_level()->load_gfx();
53   activate_bad_guys();
54   activate_objects();
55   activate_particle_systems();
56   get_level()->load_song();
57
58   apply_bonuses();
59
60   scrolling_timer.init(true);
61 }
62
63 World::World(const std::string& subset, int level_nr)
64 {
65   // FIXME: Move this to action and draw and everywhere else where the
66   // world calls child functions
67   current_ = this;
68
69   level = new Level(subset, level_nr);
70   tux.init();
71
72   set_defaults();
73
74   get_level()->load_gfx();
75   activate_bad_guys();
76   activate_objects();
77   activate_particle_systems();
78   get_level()->load_song();
79
80   apply_bonuses();
81
82   scrolling_timer.init(true);
83 }
84
85 void
86 World::apply_bonuses()
87 {
88   // Apply bonuses from former levels
89   switch (player_status.bonus)
90     {
91     case PlayerStatus::NO_BONUS:
92       break;
93
94     case PlayerStatus::FLOWER_BONUS:
95       tux.got_power = tux.FIRE_POWER;  // FIXME: add ice power to here
96       // fall through
97
98     case PlayerStatus::GROWUP_BONUS:
99       // FIXME: Move this to Player class
100       tux.size = BIG;
101       tux.base.height = 64;
102       tux.base.y -= 32;
103       break;
104     }
105 }
106
107 World::~World()
108 {
109   for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
110     delete *i;
111
112   for (Trampolines::iterator i = trampolines.begin(); i != trampolines.end(); ++i)
113     delete *i;
114
115   for (ParticleSystems::iterator i = particle_systems.begin();
116           i != particle_systems.end(); ++i)
117     delete *i;
118
119   for (std::vector<BouncyDistro*>::iterator i = bouncy_distros.begin();
120        i != bouncy_distros.end(); ++i)
121     delete *i;
122   
123   for (std::vector<BrokenBrick*>::iterator i = broken_bricks.begin();
124        i != broken_bricks.end(); ++i)
125     delete *i;
126   
127   for (std::vector<BouncyBrick*>::iterator i = bouncy_bricks.begin();
128        i != bouncy_bricks.end(); ++i)
129     delete *i;
130
131   for (std::vector<FloatingScore*>::iterator i = floating_scores.begin();
132        i != floating_scores.end(); ++i)
133     delete *i;
134   
135   delete level;
136 }
137
138 void
139 World::set_defaults()
140 {
141   // Set defaults: 
142   scroll_x = 0;
143
144   player_status.score_multiplier = 1;
145
146   counting_distros = false;
147   distro_counter = 0;
148
149   /* set current song/music */
150   currentmusic = LEVEL_MUSIC;
151 }
152
153 void
154 World::activate_bad_guys()
155 {
156   for (std::vector<BadGuyData>::iterator i = level->badguy_data.begin();
157        i != level->badguy_data.end();
158        ++i)
159     {
160       add_bad_guy(i->x, i->y, i->kind, i->stay_on_platform);
161     }
162 }
163
164 void
165 World::activate_objects()
166 {
167   for (std::vector< ObjectData<TrampolineData> >::iterator i = level->trampoline_data.begin();
168        i != level->trampoline_data.end();
169        ++i)
170   {
171     add_object<Trampoline, ObjectData<TrampolineData> >(*i);
172   }
173 }
174
175 void
176 World::activate_particle_systems()
177 {
178   if (level->particle_system == "clouds")
179     {
180       particle_systems.push_back(new CloudParticleSystem);
181     }
182   else if (level->particle_system == "snow")
183     {
184       particle_systems.push_back(new SnowParticleSystem);
185     }
186   else if (level->particle_system != "")
187     {
188       st_abort("unknown particle system specified in level", "");
189     }
190 }
191
192 void
193 World::draw()
194 {
195   int y,x;
196
197   /* Draw the real background */
198   drawgradient(level->bkgd_top, level->bkgd_bottom);
199   if(level->img_bkgd)
200       level->draw_bg();
201
202     
203   /* Draw particle systems (background) */
204   std::vector<ParticleSystem*>::iterator p;
205   for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
206     {
207       (*p)->draw(scroll_x, 0, 0);
208     }
209
210   /* Draw background: */
211   for (y = 0; y < VISIBLE_TILES_Y && y < level->height; ++y)
212     {
213       for (x = 0; x < VISIBLE_TILES_X; ++x)
214         {
215           Tile::draw(32*x - fmodf(scroll_x, 32), y * 32 - fmodf(scroll_y, 32),
216                      level->bg_tiles[(int)y + (int)(scroll_y / 32)][(int)x + (int)(scroll_x / 32)]);
217         }
218     }
219
220   /* Draw interactive tiles: */
221   for (y = 0; y < VISIBLE_TILES_Y && y < level->height; ++y)
222     {
223       for (x = 0; x < VISIBLE_TILES_X; ++x)
224         {
225           Tile::draw(32*x - fmodf(scroll_x, 32), y * 32 - fmodf(scroll_y, 32),
226                      level->ia_tiles[(int)y + (int)(scroll_y / 32)][(int)x + (int)(scroll_x / 32)]);
227         }
228     }
229
230   /* (Bouncy bricks): */
231   for (unsigned int i = 0; i < bouncy_bricks.size(); ++i)
232     bouncy_bricks[i]->draw();
233
234   for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
235     (*i)->draw();
236
237   for (Trampolines::iterator i = trampolines.begin(); i != trampolines.end(); ++i)
238     (*i)->draw();
239
240   tux.draw();
241
242   for (unsigned int i = 0; i < bullets.size(); ++i)
243     bullets[i].draw();
244
245   for (unsigned int i = 0; i < floating_scores.size(); ++i)
246     floating_scores[i]->draw();
247
248   for (unsigned int i = 0; i < upgrades.size(); ++i)
249     upgrades[i].draw();
250
251   for (unsigned int i = 0; i < bouncy_distros.size(); ++i)
252     bouncy_distros[i]->draw();
253
254   for (unsigned int i = 0; i < broken_bricks.size(); ++i)
255     broken_bricks[i]->draw();
256
257   /* Draw foreground: */
258   for (y = 0; y < VISIBLE_TILES_Y && y < level->height; ++y)
259     {
260       for (x = 0; x < VISIBLE_TILES_X; ++x)
261         {
262           Tile::draw(32*x - fmodf(scroll_x, 32), y * 32 - fmodf(scroll_y, 32),
263                      level->fg_tiles[(int)y + (int)(scroll_y / 32)][(int)x + (int)(scroll_x / 32)]);
264         }
265     }
266
267   /* Draw particle systems (foreground) */
268   for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
269     {
270       (*p)->draw(scroll_x, 0, 1);
271     }
272 }
273
274 void
275 World::action(double frame_ratio)
276 {
277   tux.action(frame_ratio);
278   tux.check_bounds(level->back_scrolling, (bool)level->hor_autoscroll_speed);
279   scrolling(frame_ratio);
280
281   /* Handle bouncy distros: */
282   for (unsigned int i = 0; i < bouncy_distros.size(); i++)
283     bouncy_distros[i]->action(frame_ratio);
284
285   /* Handle broken bricks: */
286   for (unsigned int i = 0; i < broken_bricks.size(); i++)
287     broken_bricks[i]->action(frame_ratio);
288
289   // Handle all kinds of game objects
290   for (unsigned int i = 0; i < bouncy_bricks.size(); i++)
291     bouncy_bricks[i]->action(frame_ratio);
292   
293   for (unsigned int i = 0; i < floating_scores.size(); i++)
294     floating_scores[i]->action(frame_ratio);
295
296   for (unsigned int i = 0; i < bullets.size(); ++i)
297     bullets[i].action(frame_ratio);
298   
299   for (unsigned int i = 0; i < upgrades.size(); i++)
300     upgrades[i].action(frame_ratio);
301
302   for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
303     (*i)->action(frame_ratio);
304
305   for (Trampolines::iterator i = trampolines.begin(); i != trampolines.end(); ++i)
306      (*i)->action(frame_ratio);
307
308   /* update particle systems */
309   std::vector<ParticleSystem*>::iterator p;
310   for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
311     {
312       (*p)->simulate(frame_ratio);
313     }
314
315   /* Handle all possible collisions. */
316   collision_handler();
317   
318   // Cleanup marked badguys
319   for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end();
320       /* ++i handled at end of the loop */) {
321     if ((*i)->is_removable()) {
322       delete *i;
323       i =  bad_guys.erase(i);
324     } else {
325       ++i;
326     }
327   }
328 }
329
330 /* the space that it takes for the screen to start scrolling, regarding */
331 /* screen bounds (in pixels) */
332 // should be higher than screen->w/2 (400)
333 #define X_SPACE (500-16)
334 // should be less than screen->h/2 (300)
335 #define Y_SPACE 250
336
337 // the time it takes to move the camera (in ms)
338 #define CHANGE_DIR_SCROLL_SPEED 2000
339
340 /* This functions takes cares of the scrolling */
341 void World::scrolling(double frame_ratio)
342 {
343   /* Y-axis scrolling */
344
345   float tux_pos_y = tux.base.y + (tux.base.height/2);
346
347   if(level->height > VISIBLE_TILES_Y-1 && !tux.dying)
348     {
349     if (scroll_y < tux_pos_y - (screen->h - Y_SPACE))
350       scroll_y = tux_pos_y - (screen->h - Y_SPACE);
351     else if (scroll_y > tux_pos_y - Y_SPACE)
352       scroll_y = tux_pos_y - Y_SPACE;
353     }
354
355   // this code prevent the screen to scroll before the start or after the level's end
356   if(scroll_y > level->height * 32 - screen->h)
357     scroll_y = level->height * 32 - screen->h;
358   if(scroll_y < 0)
359     scroll_y = 0;
360
361   /* X-axis scrolling */
362
363   /* Auto scrolling */
364   if(level->hor_autoscroll_speed)
365   {
366     scroll_x += level->hor_autoscroll_speed * frame_ratio;
367     return;
368   }
369
370
371   /* Horizontal backscrolling */
372   float tux_pos_x = tux.base.x + (tux.base.width/2);
373
374   if(tux.old_dir != tux.dir && level->back_scrolling)
375     scrolling_timer.start(CHANGE_DIR_SCROLL_SPEED);
376
377   bool right = false;
378   bool left = false;
379   if (tux.physic.get_velocity_x() > 0)
380     right = true;
381   else if (tux.physic.get_velocity_x() < 0)
382     left = true;
383   else
384     {
385     if (tux.dir == RIGHT)
386       right = true;
387     else
388       left = true;
389     }
390
391   if(scrolling_timer.check())
392   {
393     float final_scroll_x;
394     float constant1;
395     float constant2;
396     if (right)
397       final_scroll_x = tux_pos_x - (screen->w - X_SPACE);
398     else
399       final_scroll_x = tux_pos_x - X_SPACE;
400
401     if((tux.physic.get_velocity_x() > 0 && tux.dir == RIGHT) || (tux.physic.get_velocity_x() < 0 && tux.dir == LEFT))
402     {
403       constant1 = 1.0;
404       constant2 = .4;
405     }
406     else
407     {
408       constant1 = 0.;
409       constant2 = 0.;
410     }
411     
412     float number = 2.5/(frame_ratio * CHANGE_DIR_SCROLL_SPEED/1000)*exp((CHANGE_DIR_SCROLL_SPEED-scrolling_timer.get_left())/1400.);
413     if(left) number *= -1.;
414
415     scroll_x += number
416             + constant1 * tux.physic.get_velocity_x() * frame_ratio
417             + constant2 * tux.physic.get_acceleration_x() * frame_ratio * frame_ratio;
418
419     if ((right && final_scroll_x - scroll_x < 0) || (left && final_scroll_x - scroll_x > 0))
420       scroll_x = final_scroll_x;
421     
422   }
423   else
424   {
425     if (right && scroll_x < tux_pos_x - (screen->w - X_SPACE))
426       scroll_x = tux_pos_x - (screen->w - X_SPACE);
427     else if (left && scroll_x > tux_pos_x - X_SPACE && level->back_scrolling)
428       scroll_x = tux_pos_x - X_SPACE;
429   }
430
431   // this code prevent the screen to scroll before the start or after the level's end
432   if(scroll_x > level->width * 32 - screen->w)
433     scroll_x = level->width * 32 - screen->w;
434   if(scroll_x < 0)
435     scroll_x = 0;
436 }
437
438 void
439 World::collision_handler()
440 {
441   // CO_BULLET & CO_BADGUY check
442   for(unsigned int i = 0; i < bullets.size(); ++i)
443     {
444       for (BadGuys::iterator j = bad_guys.begin(); j != bad_guys.end(); ++j)
445         {
446           if((*j)->dying != DYING_NOT)
447             continue;
448           
449           if(rectcollision(bullets[i].base, (*j)->base))
450             {
451               // We have detected a collision and now call the
452               // collision functions of the collided objects.
453               // collide with bad_guy first, since bullet_collision will
454               // delete the bullet
455               (*j)->collision(&bullets[i], CO_BULLET);
456               bullets[i].collision(CO_BADGUY);
457               break; // bullet is invalid now, so break
458             }
459         }
460     }
461
462   /* CO_BADGUY & CO_BADGUY check */
463   for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
464     {
465       if((*i)->dying != DYING_NOT)
466         continue;
467       
468       BadGuys::iterator j = i;
469       ++j;
470       for (; j != bad_guys.end(); ++j)
471         {
472           if(j == i || (*j)->dying != DYING_NOT)
473             continue;
474
475           if(rectcollision((*i)->base, (*j)->base))
476             {
477               // We have detected a collision and now call the
478               // collision functions of the collided objects.
479               (*j)->collision(*i, CO_BADGUY);
480               (*i)->collision(*j, CO_BADGUY);
481             }
482         }
483     }
484
485   if(tux.dying != DYING_NOT) return;
486     
487   // CO_BADGUY & CO_PLAYER check 
488   for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
489     {
490       if((*i)->dying != DYING_NOT)
491         continue;
492       
493       if(rectcollision_offset((*i)->base, tux.base, 0, 0))
494         {
495           // We have detected a collision and now call the collision
496           // functions of the collided objects.
497           if (tux.previous_base.y < tux.base.y &&
498               tux.previous_base.y + tux.previous_base.height 
499               < (*i)->base.y + (*i)->base.height/2
500               && !tux.invincible_timer.started())
501             {
502               (*i)->collision(&tux, CO_PLAYER, COLLISION_SQUISH);
503             }
504           else
505             {
506               tux.collision(*i, CO_BADGUY);
507               (*i)->collision(&tux, CO_PLAYER, COLLISION_NORMAL);
508             }
509         }
510     }
511
512   // CO_UPGRADE & CO_PLAYER check
513   for(unsigned int i = 0; i < upgrades.size(); ++i)
514     {
515       if(rectcollision(upgrades[i].base, tux.base))
516         {
517           // We have detected a collision and now call the collision
518           // functions of the collided objects.
519           upgrades[i].collision(&tux, CO_PLAYER, COLLISION_NORMAL);
520         }
521     }
522
523   // CO_TRAMPOLINE & (CO_PLAYER or CO_BADGUY)
524   for (Trampolines::iterator i = trampolines.begin(); i != trampolines.end(); ++i)
525   {
526     if (rectcollision((*i)->base, tux.base))
527     {
528       if (tux.previous_base.y < tux.base.y &&
529           tux.previous_base.y + tux.previous_base.height 
530           < (*i)->base.y + (*i)->base.height/2)
531       {
532         (*i)->collision(&tux, CO_PLAYER, COLLISION_SQUISH);
533       }
534       else if (tux.previous_base.y <= tux.base.y)
535       {
536         tux.collision(*i, CO_TRAMPOLINE);
537         (*i)->collision(&tux, CO_PLAYER, COLLISION_NORMAL);
538       }
539     }
540   }
541 }
542
543 void
544 World::add_score(float x, float y, int s)
545 {
546   player_status.score += s;
547
548   FloatingScore* new_floating_score = new FloatingScore();
549   new_floating_score->init(x-scroll_x, y-scroll_y, s);
550   floating_scores.push_back(new_floating_score);
551 }
552
553 void
554 World::add_bouncy_distro(float x, float y)
555 {
556   BouncyDistro* new_bouncy_distro = new BouncyDistro();
557   new_bouncy_distro->init(x, y);
558   bouncy_distros.push_back(new_bouncy_distro);
559 }
560
561 void
562 World::add_broken_brick(Tile* tile, float x, float y)
563 {
564   add_broken_brick_piece(tile, x, y, -1, -4);
565   add_broken_brick_piece(tile, x, y + 16, -1.5, -3);
566
567   add_broken_brick_piece(tile, x + 16, y, 1, -4);
568   add_broken_brick_piece(tile, x + 16, y + 16, 1.5, -3);
569 }
570
571 void
572 World::add_broken_brick_piece(Tile* tile, float x, float y, float xm, float ym)
573 {
574   BrokenBrick* new_broken_brick = new BrokenBrick();
575   new_broken_brick->init(tile, x, y, xm, ym);
576   broken_bricks.push_back(new_broken_brick);
577 }
578
579 void
580 World::add_bouncy_brick(float x, float y)
581 {
582   BouncyBrick* new_bouncy_brick = new BouncyBrick();
583   new_bouncy_brick->init(x,y);
584   bouncy_bricks.push_back(new_bouncy_brick);
585 }
586
587 BadGuy*
588 World::add_bad_guy(float x, float y, BadGuyKind kind, bool stay_on_platform)
589 {
590   BadGuy* badguy = new BadGuy(x,y,kind, stay_on_platform);
591   bad_guys.push_back(badguy);
592   return badguy;
593 }
594
595 template<class T, class U>
596 T*
597 World::add_object(U data)
598 {
599   T* tobject = new T(data);
600
601   if (data.type == OBJ_TRAMPOLINE)
602     trampolines.push_back(tobject);
603
604   return tobject;
605 }
606
607 void
608 World::add_upgrade(float x, float y, Direction dir, UpgradeKind kind)
609 {
610   Upgrade new_upgrade;
611   new_upgrade.init(x,y,dir,kind);
612   upgrades.push_back(new_upgrade);
613 }
614
615 void 
616 World::add_bullet(float x, float y, float xm, Direction dir)
617 {
618   if(tux.got_power == tux.FIRE_POWER)
619     {
620     if(bullets.size() > MAX_FIRE_BULLETS-1)
621       return;
622     }
623   else if(tux.got_power == tux.ICE_POWER)
624     {
625     if(bullets.size() > MAX_ICE_BULLETS-1)
626       return;
627     }
628
629   Bullet new_bullet;
630   if(tux.got_power == tux.FIRE_POWER)
631     new_bullet.init(x,y,xm,dir, FIRE_BULLET);
632   else if(tux.got_power == tux.ICE_POWER)
633     new_bullet.init(x,y,xm,dir, ICE_BULLET);
634   bullets.push_back(new_bullet);
635   
636   play_sound(sounds[SND_SHOOT], SOUND_CENTER_SPEAKER);
637 }
638
639 void
640 World::play_music(int musictype)
641 {
642   currentmusic = musictype;
643   switch(currentmusic) {
644     case HURRYUP_MUSIC:
645       music_manager->play_music(get_level()->get_level_music_fast());
646       break;
647     case LEVEL_MUSIC:
648       music_manager->play_music(get_level()->get_level_music());
649       break;
650     case HERRING_MUSIC:
651       music_manager->play_music(herring_song);
652       break;
653     default:
654       music_manager->halt_music();
655       break;
656   }
657 }
658
659 int
660 World::get_music_type()
661 {
662   return currentmusic;
663 }
664
665 /* Break a brick: */
666 bool
667 World::trybreakbrick(float x, float y, bool small)
668 {
669   Level* plevel = get_level();
670   
671   Tile* tile = gettile(x, y);
672   if (tile->brick)
673     {
674       if (tile->data > 0)
675         {
676           /* Get a distro from it: */
677           add_bouncy_distro(((int)(x + 1) / 32) * 32,
678                                   (int)(y / 32) * 32);
679
680           // TODO: don't handle this in a global way but per-tile...
681           if (!counting_distros)
682             {
683               counting_distros = true;
684               distro_counter = 5;
685             }
686           else
687             {
688               distro_counter--;
689             }
690
691           if (distro_counter <= 0)
692             {
693               counting_distros = false;
694               plevel->change(x, y, TM_IA, tile->next_tile);
695             }
696
697           play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
698           player_status.score = player_status.score + SCORE_DISTRO;
699           player_status.distros++;
700           return true;
701         }
702       else if (!small)
703         {
704           /* Get rid of it: */
705           plevel->change(x, y, TM_IA, tile->next_tile);
706           
707           /* Replace it with broken bits: */
708           add_broken_brick(tile, 
709                                  ((int)(x + 1) / 32) * 32,
710                                  (int)(y / 32) * 32);
711           
712           /* Get some score: */
713           play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
714           player_status.score = player_status.score + SCORE_BRICK;
715           
716           return true;
717         }
718     }
719
720   return false;
721 }
722
723 /* Empty a box: */
724 void
725 World::tryemptybox(float x, float y, Direction col_side)
726 {
727   Tile* tile = gettile(x,y);
728   if (!tile->fullbox)
729     return;
730
731   // according to the collision side, set the upgrade direction
732   if(col_side == LEFT)
733     col_side = RIGHT;
734   else
735     col_side = LEFT;
736
737   int posx = ((int)(x+1) / 32) * 32;
738   int posy = (int)(y/32) * 32 - 32;
739   switch(tile->data)
740     {
741     case 1: // Box with a distro!
742       add_bouncy_distro(posx, posy);
743       play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
744       player_status.score = player_status.score + SCORE_DISTRO;
745       player_status.distros++;
746       break;
747
748     case 2: // Add a fire flower upgrade!
749       if (tux.size == SMALL)     /* Tux is small, add mints! */
750         add_upgrade(posx, posy, col_side, UPGRADE_GROWUP);
751       else     /* Tux is big, add a fireflower: */
752         add_upgrade(posx, posy, col_side, UPGRADE_FIREFLOWER);
753       play_sound(sounds[SND_UPGRADE], SOUND_CENTER_SPEAKER);
754       break;
755     
756     case 5: // Add an ice flower upgrade!
757       if (tux.size == SMALL)     /* Tux is small, add mints! */
758         add_upgrade(posx, posy, col_side, UPGRADE_GROWUP);
759       else     /* Tux is big, add an iceflower: */
760         add_upgrade(posx, posy, col_side, UPGRADE_ICEFLOWER);
761       play_sound(sounds[SND_UPGRADE], SOUND_CENTER_SPEAKER);
762       break;
763
764     case 3: // Add a golden herring
765       add_upgrade(posx, posy, col_side, UPGRADE_HERRING);
766       break;
767
768     case 4: // Add a 1up extra
769       add_upgrade(posx, posy, col_side, UPGRADE_1UP);
770       break;
771     default:
772       break;
773     }
774
775   /* Empty the box: */
776   level->change(x, y, TM_IA, tile->next_tile);
777 }
778
779 /* Try to grab a distro: */
780 void
781 World::trygrabdistro(float x, float y, int bounciness)
782 {
783   Tile* tile = gettile(x, y);
784   if (tile && tile->distro)
785     {
786       level->change(x, y, TM_IA, tile->next_tile);
787       play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
788
789       if (bounciness == BOUNCE)
790         {
791           add_bouncy_distro(((int)(x + 1) / 32) * 32,
792                                   (int)(y / 32) * 32);
793         }
794
795       player_status.score = player_status.score + SCORE_DISTRO;
796       player_status.distros++;
797     }
798 }
799
800 /* Try to bump a bad guy from below: */
801 void
802 World::trybumpbadguy(float x, float y)
803 {
804   // Bad guys: 
805   for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
806     {
807       if ((*i)->base.x >= x - 32 && (*i)->base.x <= x + 32 &&
808           (*i)->base.y >= y - 16 && (*i)->base.y <= y + 16)
809         {
810           (*i)->collision(&tux, CO_PLAYER, COLLISION_BUMP);
811         }
812     }
813
814   // Upgrades:
815   for (unsigned int i = 0; i < upgrades.size(); i++)
816     {
817       if (upgrades[i].base.height == 32 &&
818           upgrades[i].base.x >= x - 32 && upgrades[i].base.x <= x + 32 &&
819           upgrades[i].base.y >= y - 16 && upgrades[i].base.y <= y + 16)
820         {
821           upgrades[i].collision(&tux, CO_PLAYER, COLLISION_BUMP);
822         }
823     }
824 }
825
826 /* EOF */
827