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