Instead of limitating the number of bullets, according to the ones in screen, do...
[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   scrolling_timer.init(true);
59   bullets_timer.init(true);
60 }
61
62 World::World(const std::string& subset, int level_nr)
63 {
64   // FIXME: Move this to action and draw and everywhere else where the
65   // world calls child functions
66   current_ = this;
67
68   level = new Level(subset, level_nr);
69   tux.init();
70
71   set_defaults();
72
73   get_level()->load_gfx();
74   activate_bad_guys();
75   activate_particle_systems();
76   get_level()->load_song();
77
78   apply_bonuses();
79
80   scrolling_timer.init(true);
81   bullets_timer.init(true);
82 }
83
84 void
85 World::apply_bonuses()
86 {
87   // Apply bonuses from former levels
88   switch (player_status.bonus)
89     {
90     case PlayerStatus::NO_BONUS:
91       break;
92
93     case PlayerStatus::FLOWER_BONUS:
94       tux.got_coffee = true;
95       // fall through
96
97     case PlayerStatus::GROWUP_BONUS:
98       // FIXME: Move this to Player class
99       tux.size = BIG;
100       tux.base.height = 64;
101       tux.base.y -= 32;
102       break;
103     }
104 }
105
106 World::~World()
107 {
108   for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
109     delete *i;
110
111   for (ParticleSystems::iterator i = particle_systems.begin();
112           i != particle_systems.end(); ++i)
113     delete *i;
114
115   for (std::vector<BouncyDistro*>::iterator i = bouncy_distros.begin();
116        i != bouncy_distros.end(); ++i)
117     delete *i;
118   
119   for (std::vector<BrokenBrick*>::iterator i = broken_bricks.begin();
120        i != broken_bricks.end(); ++i)
121     delete *i;
122   
123   for (std::vector<BouncyBrick*>::iterator i = bouncy_bricks.begin();
124        i != bouncy_bricks.end(); ++i)
125     delete *i;
126
127   for (std::vector<FloatingScore*>::iterator i = floating_scores.begin();
128        i != floating_scores.end(); ++i)
129     delete *i;
130   
131   delete level;
132 }
133
134 void
135 World::set_defaults()
136 {
137   // Set defaults: 
138   scroll_x = 0;
139
140   player_status.score_multiplier = 1;
141
142   counting_distros = false;
143   distro_counter = 0;
144
145   /* set current song/music */
146   currentmusic = LEVEL_MUSIC;
147 }
148
149 void
150 World::activate_bad_guys()
151 {
152   for (std::vector<BadGuyData>::iterator i = level->badguy_data.begin();
153        i != level->badguy_data.end();
154        ++i)
155     {
156       add_bad_guy(i->x, i->y, i->kind, i->stay_on_platform);
157     }
158 }
159
160 void
161 World::activate_particle_systems()
162 {
163   if (level->particle_system == "clouds")
164     {
165       particle_systems.push_back(new CloudParticleSystem);
166     }
167   else if (level->particle_system == "snow")
168     {
169       particle_systems.push_back(new SnowParticleSystem);
170     }
171   else if (level->particle_system != "")
172     {
173       st_abort("unknown particle system specified in level", "");
174     }
175 }
176
177 void
178 World::draw()
179 {
180   int y,x;
181
182   /* Draw the real background */
183   if(get_level()->bkgd_image[0] != '\0')
184     {
185       int s = (int)((float)scroll_x * ((float)level->bkgd_speed/60.)) % screen->w;
186       level->img_bkgd->draw_part(s, 0,0,0,level->img_bkgd->w - s, level->img_bkgd->h);
187       level->img_bkgd->draw_part(0, 0,screen->w - s ,0,s,level->img_bkgd->h);
188     }
189   else
190     {
191       drawgradient(level->bkgd_top, level->bkgd_bottom);
192     }
193     
194   /* Draw particle systems (background) */
195   std::vector<ParticleSystem*>::iterator p;
196   for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
197     {
198       (*p)->draw(scroll_x, 0, 0);
199     }
200
201   /* Draw background: */
202   for (y = 0; y < 15; ++y)
203     {
204       for (x = 0; x < 21; ++x)
205         {
206           Tile::draw(32*x - fmodf(scroll_x, 32), y * 32,
207                      level->bg_tiles[(int)y][(int)x + (int)(scroll_x / 32)]);
208         }
209     }
210
211   /* Draw interactive tiles: */
212   for (y = 0; y < 15; ++y)
213     {
214       for (x = 0; x < 21; ++x)
215         {
216           Tile::draw(32*x - fmodf(scroll_x, 32), y * 32,
217                      level->ia_tiles[(int)y][(int)x + (int)(scroll_x / 32)]);
218         }
219     }
220
221   /* (Bouncy bricks): */
222   for (unsigned int i = 0; i < bouncy_bricks.size(); ++i)
223     bouncy_bricks[i]->draw();
224
225   for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
226     (*i)->draw();
227
228   tux.draw();
229
230   for (unsigned int i = 0; i < bullets.size(); ++i)
231     bullets[i].draw();
232
233   for (unsigned int i = 0; i < floating_scores.size(); ++i)
234     floating_scores[i]->draw();
235
236   for (unsigned int i = 0; i < upgrades.size(); ++i)
237     upgrades[i].draw();
238
239   for (unsigned int i = 0; i < bouncy_distros.size(); ++i)
240     bouncy_distros[i]->draw();
241
242   for (unsigned int i = 0; i < broken_bricks.size(); ++i)
243     broken_bricks[i]->draw();
244
245   /* Draw foreground: */
246   for (y = 0; y < 15; ++y)
247     {
248       for (x = 0; x < 21; ++x)
249         {
250           Tile::draw(32*x - fmodf(scroll_x, 32), y * 32,
251                      level->fg_tiles[(int)y][(int)x + (int)(scroll_x / 32)]);
252         }
253     }
254
255   /* Draw particle systems (foreground) */
256   for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
257     {
258       (*p)->draw(scroll_x, 0, 1);
259     }
260 }
261
262 void
263 World::action(double frame_ratio)
264 {
265   tux.action(frame_ratio);
266   tux.check_bounds(level->back_scrolling, (bool)level->hor_autoscroll_speed);
267   scrolling(frame_ratio);
268
269   /* Handle bouncy distros: */
270   for (unsigned int i = 0; i < bouncy_distros.size(); i++)
271     bouncy_distros[i]->action(frame_ratio);
272
273   /* Handle broken bricks: */
274   for (unsigned int i = 0; i < broken_bricks.size(); i++)
275     broken_bricks[i]->action(frame_ratio);
276
277   // Handle all kinds of game objects
278   for (unsigned int i = 0; i < bouncy_bricks.size(); i++)
279     bouncy_bricks[i]->action(frame_ratio);
280   
281   for (unsigned int i = 0; i < floating_scores.size(); i++)
282     floating_scores[i]->action(frame_ratio);
283
284   for (unsigned int i = 0; i < bullets.size(); ++i)
285     bullets[i].action(frame_ratio);
286   
287   for (unsigned int i = 0; i < upgrades.size(); i++)
288     upgrades[i].action(frame_ratio);
289
290   for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
291     (*i)->action(frame_ratio);
292
293   /* update particle systems */
294   std::vector<ParticleSystem*>::iterator p;
295   for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
296     {
297       (*p)->simulate(frame_ratio);
298     }
299
300   /* Handle all possible collisions. */
301   collision_handler();
302   
303   // Cleanup marked badguys
304   for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end();
305       /* ++i handled at end of the loop */) {
306     if ((*i)->is_removable()) {
307       delete *i;
308       i =  bad_guys.erase(i);
309     } else {
310       ++i;
311     }
312   }
313 }
314
315 // the space that it takes for the screen to start scrolling, regarding
316 // screen bounds (in pixels)
317 #define X_SPACE (400-16)
318 // the time it takes to move the camera (in ms)
319 #define CHANGE_DIR_SCROLL_SPEED 2000
320
321 /* This functions takes cares of the scrolling */
322 void World::scrolling(double frame_ratio)
323 {
324   if(level->hor_autoscroll_speed)
325     {
326     scroll_x += level->hor_autoscroll_speed * frame_ratio;
327     return;
328     }
329
330   int tux_pos_x = (int)(tux.base.x + (tux.base.width/2));
331
332   if (level->back_scrolling || debug_mode)
333   {
334     if(tux.old_dir != tux.dir && level->back_scrolling)
335       scrolling_timer.start(CHANGE_DIR_SCROLL_SPEED);
336
337     if(scrolling_timer.check())
338     {
339       float final_scroll_x;
340       if (tux.physic.get_velocity_x() > 0)
341         final_scroll_x = tux_pos_x - (screen->w - X_SPACE);
342       else if (tux.physic.get_velocity_x() < 0)
343         final_scroll_x = tux_pos_x - X_SPACE;
344       else
345       {
346         if (tux.dir == RIGHT)
347           final_scroll_x = tux_pos_x - (screen->w - X_SPACE);
348         else if (tux.dir == LEFT && level->back_scrolling)
349           final_scroll_x = tux_pos_x - X_SPACE;
350       }
351
352       scroll_x +=   (final_scroll_x - scroll_x)
353                   / (frame_ratio * (CHANGE_DIR_SCROLL_SPEED / 100))
354                   + (tux.physic.get_velocity_x() * frame_ratio + tux.physic.get_acceleration_x() * frame_ratio * frame_ratio);
355       // std::cerr << tux_pos_x << " " << final_scroll_x << " " << scroll_x << std::endl;
356
357     }
358     else
359     {
360       if (tux.physic.get_velocity_x() > 0 && scroll_x < tux_pos_x - (screen->w - X_SPACE))
361         scroll_x = tux_pos_x - (screen->w - X_SPACE);
362       else if (tux.physic.get_velocity_x() < 0 && scroll_x > tux_pos_x - X_SPACE && level->back_scrolling)
363         scroll_x = tux_pos_x - X_SPACE;
364       else
365       {
366         if (tux.dir == RIGHT && scroll_x < tux_pos_x - (screen->w - X_SPACE))
367             scroll_x = tux_pos_x - (screen->w - X_SPACE);
368         else if (tux.dir == LEFT && scroll_x > tux_pos_x - X_SPACE && level->back_scrolling)
369             scroll_x = tux_pos_x - X_SPACE;
370       }
371     }
372   }
373
374   else /*no debug*/
375   {
376     if (tux.physic.get_velocity_x() > 0 && scroll_x < tux_pos_x - (screen->w - X_SPACE))
377       scroll_x = tux_pos_x - (screen->w - X_SPACE);
378     else if (tux.physic.get_velocity_x() < 0 && scroll_x > tux_pos_x - X_SPACE && level->back_scrolling)
379       scroll_x = tux_pos_x - X_SPACE;
380     else
381     {
382       if (tux.dir == RIGHT && scroll_x < tux_pos_x - (screen->w - X_SPACE))
383           scroll_x = tux_pos_x - (screen->w - X_SPACE);
384       else if (tux.dir == LEFT && scroll_x > tux_pos_x - X_SPACE && level->back_scrolling)
385           scroll_x = tux_pos_x - X_SPACE;
386     }
387
388   }
389
390   // this code prevent the screen to scroll before the start or after the level's end
391   if(scroll_x < 0)
392     scroll_x = 0;
393   if(scroll_x > level->width * 32 - screen->w)
394     scroll_x = level->width * 32 - screen->w;
395 }
396
397 void
398 World::collision_handler()
399 {
400   // CO_BULLET & CO_BADGUY check
401   for(unsigned int i = 0; i < bullets.size(); ++i)
402     {
403       for (BadGuys::iterator j = bad_guys.begin(); j != bad_guys.end(); ++j)
404         {
405           if((*j)->dying != DYING_NOT)
406             continue;
407           
408           if(rectcollision(bullets[i].base, (*j)->base))
409             {
410               // We have detected a collision and now call the
411               // collision functions of the collided objects.
412               // collide with bad_guy first, since bullet_collision will
413               // delete the bullet
414               (*j)->collision(0, CO_BULLET);
415               bullets[i].collision(CO_BADGUY);
416               break; // bullet is invalid now, so break
417             }
418         }
419     }
420
421   /* CO_BADGUY & CO_BADGUY check */
422   for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
423     {
424       if((*i)->dying != DYING_NOT)
425         continue;
426       
427       BadGuys::iterator j = i;
428       ++j;
429       for (; j != bad_guys.end(); ++j)
430         {
431           if(j == i || (*j)->dying != DYING_NOT)
432             continue;
433
434           if(rectcollision((*i)->base, (*j)->base))
435             {
436               // We have detected a collision and now call the
437               // collision functions of the collided objects.
438               (*j)->collision(*i, CO_BADGUY);
439               (*i)->collision(*j, CO_BADGUY);
440             }
441         }
442     }
443
444   if(tux.dying != DYING_NOT) return;
445     
446   // CO_BADGUY & CO_PLAYER check 
447   for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
448     {
449       if((*i)->dying != DYING_NOT)
450         continue;
451       
452       if(rectcollision_offset((*i)->base, tux.base, 0, 0))
453         {
454           // We have detected a collision and now call the collision
455           // functions of the collided objects.
456           if (tux.previous_base.y < tux.base.y &&
457               tux.previous_base.y + tux.previous_base.height 
458               < (*i)->base.y + (*i)->base.height/2
459               && !tux.invincible_timer.started())
460             {
461               (*i)->collision(&tux, CO_PLAYER, COLLISION_SQUISH);
462             }
463           else
464             {
465               tux.collision(*i, CO_BADGUY);
466               (*i)->collision(&tux, CO_PLAYER, COLLISION_NORMAL);
467             }
468         }
469     }
470
471   // CO_UPGRADE & CO_PLAYER check
472   for(unsigned int i = 0; i < upgrades.size(); ++i)
473     {
474       if(rectcollision(upgrades[i].base, tux.base))
475         {
476           // We have detected a collision and now call the collision
477           // functions of the collided objects.
478           upgrades[i].collision(&tux, CO_PLAYER, COLLISION_NORMAL);
479         }
480     }
481 }
482
483 void
484 World::add_score(float x, float y, int s)
485 {
486   player_status.score += s;
487
488   FloatingScore* new_floating_score = new FloatingScore();
489   new_floating_score->init(x,y,s);
490   floating_scores.push_back(new_floating_score);
491 }
492
493 void
494 World::add_bouncy_distro(float x, float y)
495 {
496   BouncyDistro* new_bouncy_distro = new BouncyDistro();
497   new_bouncy_distro->init(x,y);
498   bouncy_distros.push_back(new_bouncy_distro);
499 }
500
501 void
502 World::add_broken_brick(Tile* tile, float x, float y)
503 {
504   add_broken_brick_piece(tile, x, y, -1, -4);
505   add_broken_brick_piece(tile, x, y + 16, -1.5, -3);
506
507   add_broken_brick_piece(tile, x + 16, y, 1, -4);
508   add_broken_brick_piece(tile, x + 16, y + 16, 1.5, -3);
509 }
510
511 void
512 World::add_broken_brick_piece(Tile* tile, float x, float y, float xm, float ym)
513 {
514   BrokenBrick* new_broken_brick = new BrokenBrick();
515   new_broken_brick->init(tile, x, y, xm, ym);
516   broken_bricks.push_back(new_broken_brick);
517 }
518
519 void
520 World::add_bouncy_brick(float x, float y)
521 {
522   BouncyBrick* new_bouncy_brick = new BouncyBrick();
523   new_bouncy_brick->init(x,y);
524   bouncy_bricks.push_back(new_bouncy_brick);
525 }
526
527 BadGuy*
528 World::add_bad_guy(float x, float y, BadGuyKind kind, bool stay_on_platform)
529 {
530   BadGuy* badguy = new BadGuy(x,y,kind, stay_on_platform);
531   bad_guys.push_back(badguy);
532   return badguy;
533 }
534
535 void
536 World::add_upgrade(float x, float y, Direction dir, UpgradeKind kind)
537 {
538   Upgrade new_upgrade;
539   new_upgrade.init(x,y,dir,kind);
540   upgrades.push_back(new_upgrade);
541 }
542
543 void 
544 World::add_bullet(float x, float y, float xm, Direction dir)
545 {
546   if(bullets_timer.check())
547     return;
548
549   bullets_timer.start(BULLETS_TIMEOUT);
550
551   Bullet new_bullet;
552   new_bullet.init(x,y,xm,dir);
553   bullets.push_back(new_bullet);
554   
555   play_sound(sounds[SND_SHOOT], SOUND_CENTER_SPEAKER);
556 }
557
558 void
559 World::play_music(int musictype)
560 {
561   currentmusic = musictype;
562   switch(currentmusic) {
563     case HURRYUP_MUSIC:
564       music_manager->play_music(get_level()->get_level_music_fast());
565       break;
566     case LEVEL_MUSIC:
567       music_manager->play_music(get_level()->get_level_music());
568       break;
569     case HERRING_MUSIC:
570       music_manager->play_music(herring_song);
571       break;
572     default:
573       music_manager->halt_music();
574       break;
575   }
576 }
577
578 int
579 World::get_music_type()
580 {
581   return currentmusic;
582 }
583
584 /* Break a brick: */
585 void
586 World::trybreakbrick(float x, float y, bool small)
587 {
588   Level* plevel = get_level();
589   
590   Tile* tile = gettile(x, y);
591   if (tile->brick)
592     {
593       if (tile->data > 0)
594         {
595           /* Get a distro from it: */
596           add_bouncy_distro(((int)(x + 1) / 32) * 32,
597                                   (int)(y / 32) * 32);
598
599           // TODO: don't handle this in a global way but per-tile...
600           if (!counting_distros)
601             {
602               counting_distros = true;
603               distro_counter = 5;
604             }
605           else
606             {
607               distro_counter--;
608             }
609
610           if (distro_counter <= 0)
611             {
612               counting_distros = false;
613               plevel->change(x, y, TM_IA, tile->next_tile);
614             }
615
616           play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
617           player_status.score = player_status.score + SCORE_DISTRO;
618           player_status.distros++;
619         }
620       else if (!small)
621         {
622           /* Get rid of it: */
623           plevel->change(x, y, TM_IA, tile->next_tile);
624           
625           /* Replace it with broken bits: */
626           add_broken_brick(tile, 
627                                  ((int)(x + 1) / 32) * 32,
628                                  (int)(y / 32) * 32);
629           
630           /* Get some score: */
631           play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
632           player_status.score = player_status.score + SCORE_BRICK;
633         }
634     }
635 }
636
637 /* Empty a box: */
638 void
639 World::tryemptybox(float x, float y, Direction col_side)
640 {
641   Tile* tile = gettile(x,y);
642   if (!tile->fullbox)
643     return;
644
645   // according to the collision side, set the upgrade direction
646   if(col_side == LEFT)
647     col_side = RIGHT;
648   else
649     col_side = LEFT;
650
651   int posx = ((int)(x+1) / 32) * 32;
652   int posy = (int)(y/32) * 32 - 32;
653   switch(tile->data)
654     {
655     case 1: // Box with a distro!
656       add_bouncy_distro(posx, posy);
657       play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
658       player_status.score = player_status.score + SCORE_DISTRO;
659       player_status.distros++;
660       break;
661
662     case 2: // Add an upgrade!
663       if (tux.size == SMALL)     /* Tux is small, add mints! */
664         add_upgrade(posx, posy, col_side, UPGRADE_GROWUP);
665       else     /* Tux is big, add an iceflower: */
666         add_upgrade(posx, posy, col_side, UPGRADE_ICEFLOWER);
667       play_sound(sounds[SND_UPGRADE], SOUND_CENTER_SPEAKER);
668       break;
669
670     case 3: // Add a golden herring
671       add_upgrade(posx, posy, col_side, UPGRADE_HERRING);
672       break;
673
674     case 4: // Add a 1up extra
675       add_upgrade(posx, posy, col_side, UPGRADE_1UP);
676       break;
677     default:
678       break;
679     }
680
681   /* Empty the box: */
682   level->change(x, y, TM_IA, tile->next_tile);
683 }
684
685 /* Try to grab a distro: */
686 void
687 World::trygrabdistro(float x, float y, int bounciness)
688 {
689   Tile* tile = gettile(x, y);
690   if (tile && tile->distro)
691     {
692       level->change(x, y, TM_IA, tile->next_tile);
693       play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
694
695       if (bounciness == BOUNCE)
696         {
697           add_bouncy_distro(((int)(x + 1) / 32) * 32,
698                                   (int)(y / 32) * 32);
699         }
700
701       player_status.score = player_status.score + SCORE_DISTRO;
702       player_status.distros++;
703     }
704 }
705
706 /* Try to bump a bad guy from below: */
707 void
708 World::trybumpbadguy(float x, float y)
709 {
710   // Bad guys: 
711   for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
712     {
713       if ((*i)->base.x >= x - 32 && (*i)->base.x <= x + 32 &&
714           (*i)->base.y >= y - 16 && (*i)->base.y <= y + 16)
715         {
716           (*i)->collision(&tux, CO_PLAYER, COLLISION_BUMP);
717         }
718     }
719
720   // Upgrades:
721   for (unsigned int i = 0; i < upgrades.size(); i++)
722     {
723       if (upgrades[i].base.height == 32 &&
724           upgrades[i].base.x >= x - 32 && upgrades[i].base.x <= x + 32 &&
725           upgrades[i].base.y >= y - 16 && upgrades[i].base.y <= y + 16)
726         {
727           upgrades[i].collision(&tux, CO_PLAYER, COLLISION_BUMP);
728         }
729     }
730 }
731
732 /* EOF */
733