- changed scroll speed for background image (should be configurable via level file)
[supertux.git] / src / world.cpp
1 //  $Id$
2 // 
3 //  SuperTux
4 //  Copyright (C) 2004 SuperTux Development Team, see AUTHORS for details
5 //
6 //  This program is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU General Public License
8 //  as published by the Free Software Foundation; either version 2
9 //  of the License, or (at your option) any later version.
10 //
11 //  This program is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //  GNU General Public License for more details.
15 // 
16 //  You should have received a copy of the GNU General Public License
17 //  along with this program; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19
20 #include <math.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include "globals.h"
24 #include "scene.h"
25 #include "screen.h"
26 #include "defines.h"
27 #include "world.h"
28 #include "level.h"
29 #include "tile.h"
30 #include "resources.h"
31
32 Surface* img_distro[4];
33
34 World* World::current_ = 0;
35
36 World::World()
37 {
38   // FIXME: Move this to action and draw and everywhere else where the
39   // world calls child functions
40   current_ = this;
41
42   level = new Level;
43   tux.init();
44 }
45
46 World::~World()
47 {
48   delete level;
49 }
50
51 void
52 World::set_defaults()
53 {
54   // Set defaults: 
55   scroll_x = 0;
56
57   player_status.score_multiplier = 1;
58
59   counting_distros = false;
60   distro_counter = 0;
61
62   /* set current song/music */
63   set_current_music(LEVEL_MUSIC);
64 }
65
66 int
67 World::load(const std::string& subset, int level_nr)
68 {
69   return level->load(subset, level_nr);
70 }
71
72 int
73 World::load(const std::string& filename)
74 {
75   return level->load(filename);
76 }
77
78 void
79 World::arrays_free(void)
80 {
81   bad_guys.clear();
82   bouncy_distros.clear();
83   broken_bricks.clear();
84   bouncy_bricks.clear();
85   floating_scores.clear();
86   upgrades.clear();
87   bullets.clear();
88   std::vector<ParticleSystem*>::iterator i;
89   for(i = particle_systems.begin(); i != particle_systems.end(); ++i) {
90     delete *i;
91   }
92   particle_systems.clear();
93 }
94
95 void
96 World::activate_bad_guys()
97 {
98   for (std::vector<BadGuyData>::iterator i = level->badguy_data.begin();
99        i != level->badguy_data.end();
100        ++i)
101     {
102       add_bad_guy(i->x, i->y, i->kind);
103     }
104 }
105
106 void
107 World::activate_particle_systems()
108 {
109   if (level->particle_system == "clouds")
110     {
111       particle_systems.push_back(new CloudParticleSystem);
112     }
113   else if (level->particle_system == "snow")
114     {
115       particle_systems.push_back(new SnowParticleSystem);
116     }
117   else if (level->particle_system != "")
118     {
119       st_abort("unknown particle system specified in level", "");
120     }
121 }
122
123 void
124 World::draw()
125 {
126   int y,x;
127
128   /* Draw the real background */
129   if(get_level()->bkgd_image[0] != '\0')
130     {
131       int s = ((int)scroll_x / 2)%640;
132       level->img_bkgd->draw_part(s, 0,0,0,level->img_bkgd->w - s, level->img_bkgd->h);
133       level->img_bkgd->draw_part(0, 0,screen->w - s ,0,s,level->img_bkgd->h);
134     }
135   else
136     {
137       drawgradient(level->bkgd_top, level->bkgd_bottom);
138     }
139     
140   /* Draw particle systems (background) */
141   std::vector<ParticleSystem*>::iterator p;
142   for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
143     {
144       (*p)->draw(scroll_x, 0, 0);
145     }
146
147   /* Draw background: */
148   for (y = 0; y < 15; ++y)
149     {
150       for (x = 0; x < 21; ++x)
151         {
152           Tile::draw(32*x - fmodf(scroll_x, 32), y * 32,
153                      level->bg_tiles[(int)y][(int)x + (int)(scroll_x / 32)]);
154         }
155     }
156
157   /* Draw interactive tiles: */
158   for (y = 0; y < 15; ++y)
159     {
160       for (x = 0; x < 21; ++x)
161         {
162           Tile::draw(32*x - fmodf(scroll_x, 32), y * 32,
163                      level->ia_tiles[(int)y][(int)x + (int)(scroll_x / 32)]);
164         }
165     }
166
167   /* (Bouncy bricks): */
168   for (unsigned int i = 0; i < bouncy_bricks.size(); ++i)
169     bouncy_bricks[i].draw();
170
171   for (unsigned int i = 0; i < bad_guys.size(); ++i)
172     bad_guys[i].draw();
173
174   tux.draw();
175
176   for (unsigned int i = 0; i < bullets.size(); ++i)
177     bullets[i].draw();
178
179   for (unsigned int i = 0; i < floating_scores.size(); ++i)
180     floating_scores[i].draw();
181
182   for (unsigned int i = 0; i < upgrades.size(); ++i)
183     upgrades[i].draw();
184
185   for (unsigned int i = 0; i < bouncy_distros.size(); ++i)
186     bouncy_distros[i].draw();
187
188   for (unsigned int i = 0; i < broken_bricks.size(); ++i)
189     broken_bricks[i].draw();
190
191   /* Draw foreground: */
192   for (y = 0; y < 15; ++y)
193     {
194       for (x = 0; x < 21; ++x)
195         {
196           Tile::draw(32*x - fmodf(scroll_x, 32), y * 32,
197                      level->fg_tiles[(int)y][(int)x + (int)(scroll_x / 32)]);
198         }
199     }
200
201   /* Draw particle systems (foreground) */
202   for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
203     {
204       (*p)->draw(scroll_x, 0, 1);
205     }
206 }
207
208 void
209 World::action(double frame_ratio)
210 {
211   /* Handle bouncy distros: */
212   for (unsigned int i = 0; i < bouncy_distros.size(); i++)
213     bouncy_distros[i].action(frame_ratio);
214
215   /* Handle broken bricks: */
216   for (unsigned int i = 0; i < broken_bricks.size(); i++)
217     broken_bricks[i].action(frame_ratio);
218
219   /* Handle distro counting: */
220   if (counting_distros)
221     {
222       distro_counter--;
223
224       if (distro_counter <= 0)
225         counting_distros = -1;
226     }
227
228   // Handle all kinds of game objects
229   for (unsigned int i = 0; i < bouncy_bricks.size(); i++)
230     bouncy_bricks[i].action(frame_ratio);
231   
232   for (unsigned int i = 0; i < floating_scores.size(); i++)
233     floating_scores[i].action(frame_ratio);
234
235   for (unsigned int i = 0; i < bullets.size(); ++i)
236     bullets[i].action(frame_ratio);
237   
238   for (unsigned int i = 0; i < upgrades.size(); i++)
239     upgrades[i].action(frame_ratio);
240
241   for (unsigned int i = 0; i < bad_guys.size(); i++)
242     bad_guys[i].action(frame_ratio);
243
244   /* update particle systems */
245   std::vector<ParticleSystem*>::iterator p;
246   for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
247     {
248       (*p)->simulate(frame_ratio);
249     }
250
251   /* Handle all possible collisions. */
252   collision_handler();
253 }
254
255
256 void
257 World::collision_handler()
258 {
259   // CO_BULLET & CO_BADGUY check
260   for(unsigned int i = 0; i < bullets.size(); ++i)
261     {
262       for(unsigned int j = 0; j < bad_guys.size(); ++j)
263         {
264           if(bad_guys[j].dying != DYING_NOT)
265             continue;
266           if(rectcollision(&bullets[i].base, &bad_guys[j].base))
267             {
268               // We have detected a collision and now call the
269               // collision functions of the collided objects.
270               // collide with bad_guy first, since bullet_collision will
271               // delete the bullet
272               bad_guys[j].collision(0, CO_BULLET);
273               bullets[i].collision(CO_BADGUY);
274               break; // bullet is invalid now, so break
275             }
276         }
277     }
278
279   /* CO_BADGUY & CO_BADGUY check */
280   for(unsigned int i = 0; i < bad_guys.size(); ++i)
281     {
282       if(bad_guys[i].dying != DYING_NOT)
283         continue;
284       
285       for(unsigned int j = i+1; j < bad_guys.size(); ++j)
286         {
287           if(j == i || bad_guys[j].dying != DYING_NOT)
288             continue;
289
290           if(rectcollision(&bad_guys[i].base, &bad_guys[j].base))
291             {
292               // We have detected a collision and now call the
293               // collision functions of the collided objects.
294               bad_guys[j].collision(&bad_guys[i], CO_BADGUY);
295               bad_guys[i].collision(&bad_guys[j], CO_BADGUY);
296             }
297         }
298     }
299
300   if(tux.dying != DYING_NOT) return;
301     
302   // CO_BADGUY & CO_PLAYER check 
303   for(unsigned int i = 0; i < bad_guys.size(); ++i)
304     {
305       if(bad_guys[i].dying != DYING_NOT)
306         continue;
307       
308       if(rectcollision_offset(&bad_guys[i].base,&tux.base,0,0))
309         {
310           // We have detected a collision and now call the collision
311           // functions of the collided objects.
312           if (tux.previous_base.y < tux.base.y &&
313               tux.previous_base.y + tux.previous_base.height 
314               < bad_guys[i].base.y + bad_guys[i].base.height/2)
315             {
316               bad_guys[i].collision(&tux, CO_PLAYER, COLLISION_SQUISH);
317             }
318           else
319             {
320               tux.collision(&bad_guys[i], CO_BADGUY);
321             }
322         }
323     }
324
325   // CO_UPGRADE & CO_PLAYER check
326   for(unsigned int i = 0; i < upgrades.size(); ++i)
327     {
328       if(rectcollision(&upgrades[i].base, &tux.base))
329         {
330           // We have detected a collision and now call the collision
331           // functions of the collided objects.
332           upgrades[i].collision(&tux, CO_PLAYER);
333         }
334     }
335 }
336
337 void
338 World::add_score(float x, float y, int s)
339 {
340   player_status.score += s;
341
342   FloatingScore new_floating_score;
343   new_floating_score.init(x,y,s);
344   floating_scores.push_back(new_floating_score);
345 }
346
347 void
348 World::add_bouncy_distro(float x, float y)
349 {
350   BouncyDistro new_bouncy_distro;
351   new_bouncy_distro.init(x,y);
352   bouncy_distros.push_back(new_bouncy_distro);
353 }
354
355 void
356 World::add_broken_brick(Tile* tile, float x, float y)
357 {
358   add_broken_brick_piece(tile, x, y, -1, -4);
359   add_broken_brick_piece(tile, x, y + 16, -1.5, -3);
360
361   add_broken_brick_piece(tile, x + 16, y, 1, -4);
362   add_broken_brick_piece(tile, x + 16, y + 16, 1.5, -3);
363 }
364
365 void
366 World::add_broken_brick_piece(Tile* tile, float x, float y, float xm, float ym)
367 {
368   BrokenBrick new_broken_brick;
369   new_broken_brick.init(tile, x, y, xm, ym);
370   broken_bricks.push_back(new_broken_brick);
371 }
372
373 void
374 World::add_bouncy_brick(float x, float y)
375 {
376   BouncyBrick new_bouncy_brick;
377   new_bouncy_brick.init(x,y);
378   bouncy_bricks.push_back(new_bouncy_brick);
379 }
380
381 void
382 World::add_bad_guy(float x, float y, BadGuyKind kind)
383 {
384   bad_guys.push_back(BadGuy());
385   BadGuy& new_bad_guy = bad_guys.back();
386   
387   new_bad_guy.init(x,y,kind);
388 }
389
390 void
391 World::add_upgrade(float x, float y, int dir, int kind)
392 {
393   Upgrade new_upgrade;
394   new_upgrade.init(x,y,dir,kind);
395   upgrades.push_back(new_upgrade);
396 }
397
398 void 
399 World::add_bullet(float x, float y, float xm, int dir)
400 {
401   Bullet new_bullet;
402   new_bullet.init(x,y,xm,dir);
403   bullets.push_back(new_bullet);
404   
405   play_sound(sounds[SND_SHOOT], SOUND_CENTER_SPEAKER);
406 }
407
408 /* Break a brick: */
409 void
410 World::trybreakbrick(float x, float y, bool small)
411 {
412   Level* plevel = get_level();
413   
414   Tile* tile = gettile(x, y);
415   if (tile->brick)
416     {
417       if (tile->data > 0)
418         {
419           /* Get a distro from it: */
420           add_bouncy_distro(((int)(x + 1) / 32) * 32,
421                                   (int)(y / 32) * 32);
422
423           if (!counting_distros)
424             {
425               counting_distros = true;
426               distro_counter = 50;
427             }
428
429           if (distro_counter <= 0)
430             plevel->change(x, y, TM_IA, tile->next_tile);
431
432           play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
433           player_status.score = player_status.score + SCORE_DISTRO;
434           player_status.distros++;
435         }
436       else if (!small)
437         {
438           /* Get rid of it: */
439           plevel->change(x, y, TM_IA, tile->next_tile);
440           
441           /* Replace it with broken bits: */
442           add_broken_brick(tile, 
443                                  ((int)(x + 1) / 32) * 32,
444                                  (int)(y / 32) * 32);
445           
446           /* Get some score: */
447           play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
448           player_status.score = player_status.score + SCORE_BRICK;
449         }
450     }
451 }
452
453 /* Empty a box: */
454 void
455 World::tryemptybox(float x, float y, int col_side)
456 {
457   Tile* tile = gettile(x,y);
458   if (!tile->fullbox)
459     return;
460
461   // according to the collision side, set the upgrade direction
462   if(col_side == LEFT)
463     col_side = RIGHT;
464   else
465     col_side = LEFT;
466
467   int posx = ((int)(x+1) / 32) * 32;
468   int posy = (int)(y/32) * 32 - 32;
469   switch(tile->data)
470     {
471     case 1: // Box with a distro!
472       add_bouncy_distro(posx, posy);
473       play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
474       player_status.score = player_status.score + SCORE_DISTRO;
475       player_status.distros++;
476       break;
477
478     case 2: // Add an upgrade!
479       if (tux.size == SMALL)     /* Tux is small, add mints! */
480         add_upgrade(posx, posy, col_side, UPGRADE_GROWUP);
481       else     /* Tux is big, add an iceflower: */
482         add_upgrade(posx, posy, col_side, UPGRADE_ICEFLOWER);
483       play_sound(sounds[SND_UPGRADE], SOUND_CENTER_SPEAKER);
484       break;
485
486     case 3: // Add a golden herring
487       add_upgrade(posx, posy, col_side, UPGRADE_HERRING);
488       break;
489
490     case 4: // Add a 1up extra
491       add_upgrade(posx, posy, col_side, UPGRADE_1UP);
492       break;
493     default:
494       break;
495     }
496
497   /* Empty the box: */
498   level->change(x, y, TM_IA, tile->next_tile);
499 }
500
501 /* Try to grab a distro: */
502 void
503 World::trygrabdistro(float x, float y, int bounciness)
504 {
505   Tile* tile = gettile(x, y);
506   if (tile && tile->distro)
507     {
508       level->change(x, y, TM_IA, tile->next_tile);
509       play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
510
511       if (bounciness == BOUNCE)
512         {
513           add_bouncy_distro(((int)(x + 1) / 32) * 32,
514                                   (int)(y / 32) * 32);
515         }
516
517       player_status.score = player_status.score + SCORE_DISTRO;
518       player_status.distros++;
519     }
520 }
521
522 /* Try to bump a bad guy from below: */
523 void
524 World::trybumpbadguy(float x, float y)
525 {
526   /* Bad guys: */
527   for (unsigned int i = 0; i < bad_guys.size(); i++)
528     {
529       if (bad_guys[i].base.x >= x - 32 && bad_guys[i].base.x <= x + 32 &&
530           bad_guys[i].base.y >= y - 16 && bad_guys[i].base.y <= y + 16)
531         {
532           bad_guys[i].collision(&tux, CO_PLAYER, COLLISION_BUMP);
533         }
534     }
535
536
537   /* Upgrades: */
538   for (unsigned int i = 0; i < upgrades.size(); i++)
539     {
540       if (upgrades[i].base.height == 32 &&
541           upgrades[i].base.x >= x - 32 && upgrades[i].base.x <= x + 32 &&
542           upgrades[i].base.y >= y - 16 && upgrades[i].base.y <= y + 16)
543         {
544           upgrades[i].base.xm = -upgrades[i].base.xm;
545           upgrades[i].base.ym = -8;
546           play_sound(sounds[SND_BUMP_UPGRADE], SOUND_CENTER_SPEAKER);
547         }
548     }
549 }
550
551 /* EOF */
552