e6d50fb2578157a792b6d521bbbacdf612b8a50d
[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(); ++i)
288       if ((*i)->is_removable())
289         delete *i;
290     bad_guys.remove_if(std::mem_fun(&BadGuy::is_removable));
291   }
292 }
293
294 void
295 World::collision_handler()
296 {
297   // CO_BULLET & CO_BADGUY check
298   for(unsigned int i = 0; i < bullets.size(); ++i)
299     {
300       for (BadGuys::iterator j = bad_guys.begin(); j != bad_guys.end(); ++j)
301         {
302           if((*j)->dying != DYING_NOT)
303             continue;
304           
305           if(rectcollision(bullets[i].base, (*j)->base))
306             {
307               // We have detected a collision and now call the
308               // collision functions of the collided objects.
309               // collide with bad_guy first, since bullet_collision will
310               // delete the bullet
311               (*j)->collision(0, CO_BULLET);
312               bullets[i].collision(CO_BADGUY);
313               break; // bullet is invalid now, so break
314             }
315         }
316     }
317
318   /* CO_BADGUY & CO_BADGUY check */
319   for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
320     {
321       if((*i)->dying != DYING_NOT)
322         continue;
323       
324       BadGuys::iterator j = i;
325       ++j;
326       for (; j != bad_guys.end(); ++j)
327         {
328           if(j == i || (*j)->dying != DYING_NOT)
329             continue;
330
331           if(rectcollision((*i)->base, (*j)->base))
332             {
333               // We have detected a collision and now call the
334               // collision functions of the collided objects.
335               (*j)->collision(*i, CO_BADGUY);
336               (*i)->collision(*j, CO_BADGUY);
337             }
338         }
339     }
340
341   if(tux.dying != DYING_NOT) return;
342     
343   // CO_BADGUY & CO_PLAYER check 
344   for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
345     {
346       if((*i)->dying != DYING_NOT)
347         continue;
348       
349       if(rectcollision_offset((*i)->base, tux.base, 0, 0))
350         {
351           // We have detected a collision and now call the collision
352           // functions of the collided objects.
353           if (tux.previous_base.y < tux.base.y &&
354               tux.previous_base.y + tux.previous_base.height 
355               < (*i)->base.y + (*i)->base.height/2)
356             {
357               (*i)->collision(&tux, CO_PLAYER, COLLISION_SQUISH);
358             }
359           else
360             {
361               tux.collision(*i, CO_BADGUY);
362               (*i)->collision(&tux, CO_PLAYER, COLLISION_NORMAL);
363             }
364         }
365     }
366
367   // CO_UPGRADE & CO_PLAYER check
368   for(unsigned int i = 0; i < upgrades.size(); ++i)
369     {
370       if(rectcollision(upgrades[i].base, tux.base))
371         {
372           // We have detected a collision and now call the collision
373           // functions of the collided objects.
374           upgrades[i].collision(&tux, CO_PLAYER);
375         }
376     }
377 }
378
379 void
380 World::add_score(float x, float y, int s)
381 {
382   player_status.score += s;
383
384   FloatingScore new_floating_score;
385   new_floating_score.init(x,y,s);
386   floating_scores.push_back(new_floating_score);
387 }
388
389 void
390 World::add_bouncy_distro(float x, float y)
391 {
392   BouncyDistro new_bouncy_distro;
393   new_bouncy_distro.init(x,y);
394   bouncy_distros.push_back(new_bouncy_distro);
395 }
396
397 void
398 World::add_broken_brick(Tile* tile, float x, float y)
399 {
400   add_broken_brick_piece(tile, x, y, -1, -4);
401   add_broken_brick_piece(tile, x, y + 16, -1.5, -3);
402
403   add_broken_brick_piece(tile, x + 16, y, 1, -4);
404   add_broken_brick_piece(tile, x + 16, y + 16, 1.5, -3);
405 }
406
407 void
408 World::add_broken_brick_piece(Tile* tile, float x, float y, float xm, float ym)
409 {
410   BrokenBrick new_broken_brick;
411   new_broken_brick.init(tile, x, y, xm, ym);
412   broken_bricks.push_back(new_broken_brick);
413 }
414
415 void
416 World::add_bouncy_brick(float x, float y)
417 {
418   BouncyBrick new_bouncy_brick;
419   new_bouncy_brick.init(x,y);
420   bouncy_bricks.push_back(new_bouncy_brick);
421 }
422
423 BadGuy*
424 World::add_bad_guy(float x, float y, BadGuyKind kind, bool stay_on_platform)
425 {
426   BadGuy* badguy = new BadGuy(x,y,kind, stay_on_platform);
427   bad_guys.push_back(badguy);
428   return badguy;
429 }
430
431 void
432 World::add_upgrade(float x, float y, Direction dir, UpgradeKind kind)
433 {
434   Upgrade new_upgrade;
435   new_upgrade.init(x,y,dir,kind);
436   upgrades.push_back(new_upgrade);
437 }
438
439 void 
440 World::add_bullet(float x, float y, float xm, Direction dir)
441 {
442   if(bullets.size() > MAX_BULLETS-1)
443     return;
444
445   Bullet new_bullet;
446   new_bullet.init(x,y,xm,dir);
447   bullets.push_back(new_bullet);
448   
449   play_sound(sounds[SND_SHOOT], SOUND_CENTER_SPEAKER);
450 }
451
452 void
453 World::play_music(int musictype)
454 {
455   currentmusic = musictype;
456   switch(currentmusic) {
457     case HURRYUP_MUSIC:
458       music_manager->play_music(get_level()->get_level_music_fast());
459       break;
460     case LEVEL_MUSIC:
461       music_manager->play_music(get_level()->get_level_music());
462       break;
463     case HERRING_MUSIC:
464       music_manager->play_music(herring_song);
465       break;
466     default:
467       music_manager->halt_music();
468       break;
469   }
470 }
471
472 int
473 World::get_music_type()
474 {
475   return currentmusic;
476 }
477
478 /* Break a brick: */
479 void
480 World::trybreakbrick(float x, float y, bool small)
481 {
482   Level* plevel = get_level();
483   
484   Tile* tile = gettile(x, y);
485   if (tile->brick)
486     {
487       if (tile->data > 0)
488         {
489           /* Get a distro from it: */
490           add_bouncy_distro(((int)(x + 1) / 32) * 32,
491                                   (int)(y / 32) * 32);
492
493           if (!counting_distros)
494             {
495               counting_distros = true;
496               distro_counter = 50;
497             }
498
499           if (distro_counter <= 0)
500             plevel->change(x, y, TM_IA, tile->next_tile);
501
502           play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
503           player_status.score = player_status.score + SCORE_DISTRO;
504           player_status.distros++;
505         }
506       else if (!small)
507         {
508           /* Get rid of it: */
509           plevel->change(x, y, TM_IA, tile->next_tile);
510           
511           /* Replace it with broken bits: */
512           add_broken_brick(tile, 
513                                  ((int)(x + 1) / 32) * 32,
514                                  (int)(y / 32) * 32);
515           
516           /* Get some score: */
517           play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
518           player_status.score = player_status.score + SCORE_BRICK;
519         }
520     }
521 }
522
523 /* Empty a box: */
524 void
525 World::tryemptybox(float x, float y, Direction col_side)
526 {
527   Tile* tile = gettile(x,y);
528   if (!tile->fullbox)
529     return;
530
531   // according to the collision side, set the upgrade direction
532   if(col_side == LEFT)
533     col_side = RIGHT;
534   else
535     col_side = LEFT;
536
537   int posx = ((int)(x+1) / 32) * 32;
538   int posy = (int)(y/32) * 32 - 32;
539   switch(tile->data)
540     {
541     case 1: // Box with a distro!
542       add_bouncy_distro(posx, posy);
543       play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
544       player_status.score = player_status.score + SCORE_DISTRO;
545       player_status.distros++;
546       break;
547
548     case 2: // Add an upgrade!
549       if (tux.size == SMALL)     /* Tux is small, add mints! */
550         add_upgrade(posx, posy, col_side, UPGRADE_GROWUP);
551       else     /* Tux is big, add an iceflower: */
552         add_upgrade(posx, posy, col_side, UPGRADE_ICEFLOWER);
553       play_sound(sounds[SND_UPGRADE], SOUND_CENTER_SPEAKER);
554       break;
555
556     case 3: // Add a golden herring
557       add_upgrade(posx, posy, col_side, UPGRADE_HERRING);
558       break;
559
560     case 4: // Add a 1up extra
561       add_upgrade(posx, posy, col_side, UPGRADE_1UP);
562       break;
563     default:
564       break;
565     }
566
567   /* Empty the box: */
568   level->change(x, y, TM_IA, tile->next_tile);
569 }
570
571 /* Try to grab a distro: */
572 void
573 World::trygrabdistro(float x, float y, int bounciness)
574 {
575   Tile* tile = gettile(x, y);
576   if (tile && tile->distro)
577     {
578       level->change(x, y, TM_IA, tile->next_tile);
579       play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
580
581       if (bounciness == BOUNCE)
582         {
583           add_bouncy_distro(((int)(x + 1) / 32) * 32,
584                                   (int)(y / 32) * 32);
585         }
586
587       player_status.score = player_status.score + SCORE_DISTRO;
588       player_status.distros++;
589     }
590 }
591
592 /* Try to bump a bad guy from below: */
593 void
594 World::trybumpbadguy(float x, float y)
595 {
596   // Bad guys: 
597   for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
598     {
599       if ((*i)->base.x >= x - 32 && (*i)->base.x <= x + 32 &&
600           (*i)->base.y >= y - 16 && (*i)->base.y <= y + 16)
601         {
602           (*i)->collision(&tux, CO_PLAYER, COLLISION_BUMP);
603         }
604     }
605
606   // Upgrades:
607   for (unsigned int i = 0; i < upgrades.size(); i++)
608     {
609       if (upgrades[i].base.height == 32 &&
610           upgrades[i].base.x >= x - 32 && upgrades[i].base.x <= x + 32 &&
611           upgrades[i].base.y >= y - 16 && upgrades[i].base.y <= y + 16)
612         {
613           upgrades[i].base.xm = -upgrades[i].base.xm;
614           upgrades[i].base.ym = -8;
615           play_sound(sounds[SND_BUMP_UPGRADE], SOUND_CENTER_SPEAKER);
616         }
617     }
618 }
619
620 /* EOF */
621