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