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