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