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