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