<MatzeB> -cleanup in resource management functions
[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
59 World::World(const std::string& subset, int level_nr)
60 {
61   // FIXME: Move this to action and draw and everywhere else where the
62   // world calls child functions
63   current_ = this;
64
65   level = new Level(subset, level_nr);
66   tux.init();
67
68   set_defaults();
69
70   get_level()->load_gfx();
71   activate_bad_guys();
72   activate_particle_systems();
73   get_level()->load_song();
74
75   apply_bonuses();
76 }
77
78 void
79 World::apply_bonuses()
80 {
81   std::cout << "Bonus: " << player_status.bonus << std::endl;
82
83   // Apply bonuses from former levels
84   switch (player_status.bonus)
85     {
86     case PlayerStatus::NO_BONUS:
87       break;
88
89     case PlayerStatus::FLOWER_BONUS:
90       tux.got_coffee = true;
91       // fall through
92
93     case PlayerStatus::GROWUP_BONUS:
94       // FIXME: Move this to Player class
95       tux.size = BIG;
96       tux.base.height = 64;
97       tux.base.y -= 32;
98       break;
99     }
100 }
101
102 World::~World()
103 {
104   for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
105     delete *i;
106   
107   delete level;
108 }
109
110 void
111 World::set_defaults()
112 {
113   // Set defaults: 
114   scroll_x = 0;
115
116   player_status.score_multiplier = 1;
117
118   counting_distros = false;
119   distro_counter = 0;
120
121   /* set current song/music */
122   currentmusic = LEVEL_MUSIC;
123 }
124
125 void
126 World::activate_bad_guys()
127 {
128   for (std::vector<BadGuyData>::iterator i = level->badguy_data.begin();
129        i != level->badguy_data.end();
130        ++i)
131     {
132       add_bad_guy(i->x, i->y, i->kind, i->stay_on_platform);
133     }
134 }
135
136 void
137 World::activate_particle_systems()
138 {
139   if (level->particle_system == "clouds")
140     {
141       particle_systems.push_back(new CloudParticleSystem);
142     }
143   else if (level->particle_system == "snow")
144     {
145       particle_systems.push_back(new SnowParticleSystem);
146     }
147   else if (level->particle_system != "")
148     {
149       st_abort("unknown particle system specified in level", "");
150     }
151 }
152
153 void
154 World::draw()
155 {
156   int y,x;
157
158   /* Draw the real background */
159   if(get_level()->bkgd_image[0] != '\0')
160     {
161       int s = ((int)scroll_x / 2)%640;
162       level->img_bkgd->draw_part(s, 0,0,0,level->img_bkgd->w - s, level->img_bkgd->h);
163       level->img_bkgd->draw_part(0, 0,screen->w - s ,0,s,level->img_bkgd->h);
164     }
165   else
166     {
167       drawgradient(level->bkgd_top, level->bkgd_bottom);
168     }
169     
170   /* Draw particle systems (background) */
171   std::vector<ParticleSystem*>::iterator p;
172   for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
173     {
174       (*p)->draw(scroll_x, 0, 0);
175     }
176
177   /* Draw background: */
178   for (y = 0; y < 15; ++y)
179     {
180       for (x = 0; x < 21; ++x)
181         {
182           Tile::draw(32*x - fmodf(scroll_x, 32), y * 32,
183                      level->bg_tiles[(int)y][(int)x + (int)(scroll_x / 32)]);
184         }
185     }
186
187   /* Draw interactive tiles: */
188   for (y = 0; y < 15; ++y)
189     {
190       for (x = 0; x < 21; ++x)
191         {
192           Tile::draw(32*x - fmodf(scroll_x, 32), y * 32,
193                      level->ia_tiles[(int)y][(int)x + (int)(scroll_x / 32)]);
194         }
195     }
196
197   /* (Bouncy bricks): */
198   for (unsigned int i = 0; i < bouncy_bricks.size(); ++i)
199     bouncy_bricks[i].draw();
200
201   for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
202     (*i)->draw();
203
204   tux.draw();
205
206   for (unsigned int i = 0; i < bullets.size(); ++i)
207     bullets[i].draw();
208
209   for (unsigned int i = 0; i < floating_scores.size(); ++i)
210     floating_scores[i].draw();
211
212   for (unsigned int i = 0; i < upgrades.size(); ++i)
213     upgrades[i].draw();
214
215   for (unsigned int i = 0; i < bouncy_distros.size(); ++i)
216     bouncy_distros[i].draw();
217
218   for (unsigned int i = 0; i < broken_bricks.size(); ++i)
219     broken_bricks[i].draw();
220
221   /* Draw foreground: */
222   for (y = 0; y < 15; ++y)
223     {
224       for (x = 0; x < 21; ++x)
225         {
226           Tile::draw(32*x - fmodf(scroll_x, 32), y * 32,
227                      level->fg_tiles[(int)y][(int)x + (int)(scroll_x / 32)]);
228         }
229     }
230
231   /* Draw particle systems (foreground) */
232   for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
233     {
234       (*p)->draw(scroll_x, 0, 1);
235     }
236 }
237
238 void
239 World::action(double frame_ratio)
240 {
241   tux.action(frame_ratio);
242
243   /* Handle bouncy distros: */
244   for (unsigned int i = 0; i < bouncy_distros.size(); i++)
245     bouncy_distros[i].action(frame_ratio);
246
247   /* Handle broken bricks: */
248   for (unsigned int i = 0; i < broken_bricks.size(); i++)
249     broken_bricks[i].action(frame_ratio);
250
251   /* Handle distro counting: */
252   if (counting_distros)
253     {
254       distro_counter--;
255
256       if (distro_counter <= 0)
257         counting_distros = -1;
258     }
259
260   // Handle all kinds of game objects
261   for (unsigned int i = 0; i < bouncy_bricks.size(); i++)
262     bouncy_bricks[i].action(frame_ratio);
263   
264   for (unsigned int i = 0; i < floating_scores.size(); i++)
265     floating_scores[i].action(frame_ratio);
266
267   for (unsigned int i = 0; i < bullets.size(); ++i)
268     bullets[i].action(frame_ratio);
269   
270   for (unsigned int i = 0; i < upgrades.size(); i++)
271     upgrades[i].action(frame_ratio);
272
273   for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
274     (*i)->action(frame_ratio);
275
276   /* update particle systems */
277   std::vector<ParticleSystem*>::iterator p;
278   for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
279     {
280       (*p)->simulate(frame_ratio);
281     }
282
283   /* Handle all possible collisions. */
284   collision_handler();
285   
286   // Cleanup marked badguys
287   for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end();
288       /* ++i handled at end of the loop */) {
289     if ((*i)->is_removable()) {
290       delete *i;
291       i = bad_guys.erase(i);
292     } else {
293       ++i;
294     }
295   }
296 }
297
298 void
299 World::collision_handler()
300 {
301   // CO_BULLET & CO_BADGUY check
302   for(unsigned int i = 0; i < bullets.size(); ++i)
303     {
304       for (BadGuys::iterator j = bad_guys.begin(); j != bad_guys.end(); ++j)
305         {
306           if((*j)->dying != DYING_NOT)
307             continue;
308           
309           if(rectcollision(bullets[i].base, (*j)->base))
310             {
311               // We have detected a collision and now call the
312               // collision functions of the collided objects.
313               // collide with bad_guy first, since bullet_collision will
314               // delete the bullet
315               (*j)->collision(0, CO_BULLET);
316               bullets[i].collision(CO_BADGUY);
317               break; // bullet is invalid now, so break
318             }
319         }
320     }
321
322   /* CO_BADGUY & CO_BADGUY check */
323   for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
324     {
325       if((*i)->dying != DYING_NOT)
326         continue;
327       
328       BadGuys::iterator j = i;
329       ++j;
330       for (; j != bad_guys.end(); ++j)
331         {
332           if(j == i || (*j)->dying != DYING_NOT)
333             continue;
334
335           if(rectcollision((*i)->base, (*j)->base))
336             {
337               // We have detected a collision and now call the
338               // collision functions of the collided objects.
339               (*j)->collision(*i, CO_BADGUY);
340               (*i)->collision(*j, CO_BADGUY);
341             }
342         }
343     }
344
345   if(tux.dying != DYING_NOT) return;
346     
347   // CO_BADGUY & CO_PLAYER check 
348   for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
349     {
350       if((*i)->dying != DYING_NOT)
351         continue;
352       
353       if(rectcollision_offset((*i)->base, tux.base, 0, 0))
354         {
355           // We have detected a collision and now call the collision
356           // functions of the collided objects.
357           if (tux.previous_base.y < tux.base.y &&
358               tux.previous_base.y + tux.previous_base.height 
359               < (*i)->base.y + (*i)->base.height/2)
360             {
361               (*i)->collision(&tux, CO_PLAYER, COLLISION_SQUISH);
362             }
363           else
364             {
365               tux.collision(*i, CO_BADGUY);
366               (*i)->collision(&tux, CO_PLAYER, COLLISION_NORMAL);
367             }
368         }
369     }
370
371   // CO_UPGRADE & CO_PLAYER check
372   for(unsigned int i = 0; i < upgrades.size(); ++i)
373     {
374       if(rectcollision(upgrades[i].base, tux.base))
375         {
376           // We have detected a collision and now call the collision
377           // functions of the collided objects.
378           upgrades[i].collision(&tux, CO_PLAYER);
379         }
380     }
381 }
382
383 void
384 World::add_score(float x, float y, int s)
385 {
386   player_status.score += s;
387
388   FloatingScore new_floating_score;
389   new_floating_score.init(x,y,s);
390   floating_scores.push_back(new_floating_score);
391 }
392
393 void
394 World::add_bouncy_distro(float x, float y)
395 {
396   BouncyDistro new_bouncy_distro;
397   new_bouncy_distro.init(x,y);
398   bouncy_distros.push_back(new_bouncy_distro);
399 }
400
401 void
402 World::add_broken_brick(Tile* tile, float x, float y)
403 {
404   add_broken_brick_piece(tile, x, y, -1, -4);
405   add_broken_brick_piece(tile, x, y + 16, -1.5, -3);
406
407   add_broken_brick_piece(tile, x + 16, y, 1, -4);
408   add_broken_brick_piece(tile, x + 16, y + 16, 1.5, -3);
409 }
410
411 void
412 World::add_broken_brick_piece(Tile* tile, float x, float y, float xm, float ym)
413 {
414   BrokenBrick new_broken_brick;
415   new_broken_brick.init(tile, x, y, xm, ym);
416   broken_bricks.push_back(new_broken_brick);
417 }
418
419 void
420 World::add_bouncy_brick(float x, float y)
421 {
422   BouncyBrick new_bouncy_brick;
423   new_bouncy_brick.init(x,y);
424   bouncy_bricks.push_back(new_bouncy_brick);
425 }
426
427 BadGuy*
428 World::add_bad_guy(float x, float y, BadGuyKind kind, bool stay_on_platform)
429 {
430   BadGuy* badguy = new BadGuy(x,y,kind, stay_on_platform);
431   bad_guys.push_back(badguy);
432   return badguy;
433 }
434
435 void
436 World::add_upgrade(float x, float y, Direction dir, UpgradeKind kind)
437 {
438   Upgrade new_upgrade;
439   new_upgrade.init(x,y,dir,kind);
440   upgrades.push_back(new_upgrade);
441 }
442
443 void 
444 World::add_bullet(float x, float y, float xm, Direction dir)
445 {
446   if(bullets.size() > MAX_BULLETS-1)
447     return;
448
449   Bullet new_bullet;
450   new_bullet.init(x,y,xm,dir);
451   bullets.push_back(new_bullet);
452   
453   play_sound(sounds[SND_SHOOT], SOUND_CENTER_SPEAKER);
454 }
455
456 void
457 World::play_music(int musictype)
458 {
459   currentmusic = musictype;
460   switch(currentmusic) {
461     case HURRYUP_MUSIC:
462       music_manager->play_music(get_level()->get_level_music_fast());
463       break;
464     case LEVEL_MUSIC:
465       music_manager->play_music(get_level()->get_level_music());
466       break;
467     case HERRING_MUSIC:
468       music_manager->play_music(herring_song);
469       break;
470     default:
471       music_manager->halt_music();
472       break;
473   }
474 }
475
476 int
477 World::get_music_type()
478 {
479   return currentmusic;
480 }
481
482 /* Break a brick: */
483 void
484 World::trybreakbrick(float x, float y, bool small)
485 {
486   Level* plevel = get_level();
487   
488   Tile* tile = gettile(x, y);
489   if (tile->brick)
490     {
491       if (tile->data > 0)
492         {
493           /* Get a distro from it: */
494           add_bouncy_distro(((int)(x + 1) / 32) * 32,
495                                   (int)(y / 32) * 32);
496
497           if (!counting_distros)
498             {
499               counting_distros = true;
500               distro_counter = 50;
501             }
502
503           if (distro_counter <= 0)
504             plevel->change(x, y, TM_IA, tile->next_tile);
505
506           play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
507           player_status.score = player_status.score + SCORE_DISTRO;
508           player_status.distros++;
509         }
510       else if (!small)
511         {
512           /* Get rid of it: */
513           plevel->change(x, y, TM_IA, tile->next_tile);
514           
515           /* Replace it with broken bits: */
516           add_broken_brick(tile, 
517                                  ((int)(x + 1) / 32) * 32,
518                                  (int)(y / 32) * 32);
519           
520           /* Get some score: */
521           play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
522           player_status.score = player_status.score + SCORE_BRICK;
523         }
524     }
525 }
526
527 /* Empty a box: */
528 void
529 World::tryemptybox(float x, float y, Direction col_side)
530 {
531   Tile* tile = gettile(x,y);
532   if (!tile->fullbox)
533     return;
534
535   // according to the collision side, set the upgrade direction
536   if(col_side == LEFT)
537     col_side = RIGHT;
538   else
539     col_side = LEFT;
540
541   int posx = ((int)(x+1) / 32) * 32;
542   int posy = (int)(y/32) * 32 - 32;
543   switch(tile->data)
544     {
545     case 1: // Box with a distro!
546       add_bouncy_distro(posx, posy);
547       play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
548       player_status.score = player_status.score + SCORE_DISTRO;
549       player_status.distros++;
550       break;
551
552     case 2: // Add an upgrade!
553       if (tux.size == SMALL)     /* Tux is small, add mints! */
554         add_upgrade(posx, posy, col_side, UPGRADE_GROWUP);
555       else     /* Tux is big, add an iceflower: */
556         add_upgrade(posx, posy, col_side, UPGRADE_ICEFLOWER);
557       play_sound(sounds[SND_UPGRADE], SOUND_CENTER_SPEAKER);
558       break;
559
560     case 3: // Add a golden herring
561       add_upgrade(posx, posy, col_side, UPGRADE_HERRING);
562       break;
563
564     case 4: // Add a 1up extra
565       add_upgrade(posx, posy, col_side, UPGRADE_1UP);
566       break;
567     default:
568       break;
569     }
570
571   /* Empty the box: */
572   level->change(x, y, TM_IA, tile->next_tile);
573 }
574
575 /* Try to grab a distro: */
576 void
577 World::trygrabdistro(float x, float y, int bounciness)
578 {
579   Tile* tile = gettile(x, y);
580   if (tile && tile->distro)
581     {
582       level->change(x, y, TM_IA, tile->next_tile);
583       play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
584
585       if (bounciness == BOUNCE)
586         {
587           add_bouncy_distro(((int)(x + 1) / 32) * 32,
588                                   (int)(y / 32) * 32);
589         }
590
591       player_status.score = player_status.score + SCORE_DISTRO;
592       player_status.distros++;
593     }
594 }
595
596 /* Try to bump a bad guy from below: */
597 void
598 World::trybumpbadguy(float x, float y)
599 {
600   // Bad guys: 
601   for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
602     {
603       if ((*i)->base.x >= x - 32 && (*i)->base.x <= x + 32 &&
604           (*i)->base.y >= y - 16 && (*i)->base.y <= y + 16)
605         {
606           (*i)->collision(&tux, CO_PLAYER, COLLISION_BUMP);
607         }
608     }
609
610   // Upgrades:
611   for (unsigned int i = 0; i < upgrades.size(); i++)
612     {
613       if (upgrades[i].base.height == 32 &&
614           upgrades[i].base.x >= x - 32 && upgrades[i].base.x <= x + 32 &&
615           upgrades[i].base.y >= y - 16 && upgrades[i].base.y <= y + 16)
616         {
617           upgrades[i].base.xm = -upgrades[i].base.xm;
618           upgrades[i].base.ym = -8;
619           play_sound(sounds[SND_BUMP_UPGRADE], SOUND_CENTER_SPEAKER);
620         }
621     }
622 }
623
624 /* EOF */
625