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