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