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