- added stay-on-platform into the level fileformat
[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   delete level;
76 }
77
78 void
79 World::set_defaults()
80 {
81   // Set defaults: 
82   scroll_x = 0;
83
84   player_status.score_multiplier = 1;
85
86   counting_distros = false;
87   distro_counter = 0;
88
89   /* set current song/music */
90   set_current_music(LEVEL_MUSIC);
91 }
92
93 void
94 World::activate_bad_guys()
95 {
96   for (std::vector<BadGuyData>::iterator i = level->badguy_data.begin();
97        i != level->badguy_data.end();
98        ++i)
99     {
100       add_bad_guy(i->x, i->y, i->kind, i->stay_on_platform);
101     }
102 }
103
104 void
105 World::activate_particle_systems()
106 {
107   if (level->particle_system == "clouds")
108     {
109       particle_systems.push_back(new CloudParticleSystem);
110     }
111   else if (level->particle_system == "snow")
112     {
113       particle_systems.push_back(new SnowParticleSystem);
114     }
115   else if (level->particle_system != "")
116     {
117       st_abort("unknown particle system specified in level", "");
118     }
119 }
120
121 void
122 World::draw()
123 {
124   int y,x;
125
126   /* Draw the real background */
127   if(get_level()->bkgd_image[0] != '\0')
128     {
129       int s = ((int)scroll_x / 2)%640;
130       level->img_bkgd->draw_part(s, 0,0,0,level->img_bkgd->w - s, level->img_bkgd->h);
131       level->img_bkgd->draw_part(0, 0,screen->w - s ,0,s,level->img_bkgd->h);
132     }
133   else
134     {
135       drawgradient(level->bkgd_top, level->bkgd_bottom);
136     }
137     
138   /* Draw particle systems (background) */
139   std::vector<ParticleSystem*>::iterator p;
140   for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
141     {
142       (*p)->draw(scroll_x, 0, 0);
143     }
144
145   /* Draw background: */
146   for (y = 0; y < 15; ++y)
147     {
148       for (x = 0; x < 21; ++x)
149         {
150           Tile::draw(32*x - fmodf(scroll_x, 32), y * 32,
151                      level->bg_tiles[(int)y][(int)x + (int)(scroll_x / 32)]);
152         }
153     }
154
155   /* Draw interactive tiles: */
156   for (y = 0; y < 15; ++y)
157     {
158       for (x = 0; x < 21; ++x)
159         {
160           Tile::draw(32*x - fmodf(scroll_x, 32), y * 32,
161                      level->ia_tiles[(int)y][(int)x + (int)(scroll_x / 32)]);
162         }
163     }
164
165   /* (Bouncy bricks): */
166   for (unsigned int i = 0; i < bouncy_bricks.size(); ++i)
167     bouncy_bricks[i].draw();
168
169   for (unsigned int i = 0; i < bad_guys.size(); ++i)
170     bad_guys[i].draw();
171
172   tux.draw();
173
174   for (unsigned int i = 0; i < bullets.size(); ++i)
175     bullets[i].draw();
176
177   for (unsigned int i = 0; i < floating_scores.size(); ++i)
178     floating_scores[i].draw();
179
180   for (unsigned int i = 0; i < upgrades.size(); ++i)
181     upgrades[i].draw();
182
183   for (unsigned int i = 0; i < bouncy_distros.size(); ++i)
184     bouncy_distros[i].draw();
185
186   for (unsigned int i = 0; i < broken_bricks.size(); ++i)
187     broken_bricks[i].draw();
188
189   /* Draw foreground: */
190   for (y = 0; y < 15; ++y)
191     {
192       for (x = 0; x < 21; ++x)
193         {
194           Tile::draw(32*x - fmodf(scroll_x, 32), y * 32,
195                      level->fg_tiles[(int)y][(int)x + (int)(scroll_x / 32)]);
196         }
197     }
198
199   /* Draw particle systems (foreground) */
200   for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
201     {
202       (*p)->draw(scroll_x, 0, 1);
203     }
204 }
205
206 void
207 World::action(double frame_ratio)
208 {
209   tux.action(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               bad_guys[i].collision(&tux, CO_PLAYER, COLLISION_NORMAL);
322             }
323         }
324     }
325
326   // CO_UPGRADE & CO_PLAYER check
327   for(unsigned int i = 0; i < upgrades.size(); ++i)
328     {
329       if(rectcollision(&upgrades[i].base, &tux.base))
330         {
331           // We have detected a collision and now call the collision
332           // functions of the collided objects.
333           upgrades[i].collision(&tux, CO_PLAYER);
334         }
335     }
336 }
337
338 void
339 World::add_score(float x, float y, int s)
340 {
341   player_status.score += s;
342
343   FloatingScore new_floating_score;
344   new_floating_score.init(x,y,s);
345   floating_scores.push_back(new_floating_score);
346 }
347
348 void
349 World::add_bouncy_distro(float x, float y)
350 {
351   BouncyDistro new_bouncy_distro;
352   new_bouncy_distro.init(x,y);
353   bouncy_distros.push_back(new_bouncy_distro);
354 }
355
356 void
357 World::add_broken_brick(Tile* tile, float x, float y)
358 {
359   add_broken_brick_piece(tile, x, y, -1, -4);
360   add_broken_brick_piece(tile, x, y + 16, -1.5, -3);
361
362   add_broken_brick_piece(tile, x + 16, y, 1, -4);
363   add_broken_brick_piece(tile, x + 16, y + 16, 1.5, -3);
364 }
365
366 void
367 World::add_broken_brick_piece(Tile* tile, float x, float y, float xm, float ym)
368 {
369   BrokenBrick new_broken_brick;
370   new_broken_brick.init(tile, x, y, xm, ym);
371   broken_bricks.push_back(new_broken_brick);
372 }
373
374 void
375 World::add_bouncy_brick(float x, float y)
376 {
377   BouncyBrick new_bouncy_brick;
378   new_bouncy_brick.init(x,y);
379   bouncy_bricks.push_back(new_bouncy_brick);
380 }
381
382 void
383 World::add_bad_guy(float x, float y, BadGuyKind kind, bool stay_on_platform)
384 {
385   bad_guys.push_back(BadGuy());
386   BadGuy& new_bad_guy = bad_guys.back();
387   
388   new_bad_guy.init(x,y,kind, stay_on_platform);
389 }
390
391 void
392 World::add_upgrade(float x, float y, Direction dir, UpgradeKind kind)
393 {
394   Upgrade new_upgrade;
395   new_upgrade.init(x,y,dir,kind);
396   upgrades.push_back(new_upgrade);
397 }
398
399 void 
400 World::add_bullet(float x, float y, float xm, Direction dir)
401 {
402   Bullet new_bullet;
403   new_bullet.init(x,y,xm,dir);
404   bullets.push_back(new_bullet);
405   
406   play_sound(sounds[SND_SHOOT], SOUND_CENTER_SPEAKER);
407 }
408
409 /* Break a brick: */
410 void
411 World::trybreakbrick(float x, float y, bool small)
412 {
413   Level* plevel = get_level();
414   
415   Tile* tile = gettile(x, y);
416   if (tile->brick)
417     {
418       if (tile->data > 0)
419         {
420           /* Get a distro from it: */
421           add_bouncy_distro(((int)(x + 1) / 32) * 32,
422                                   (int)(y / 32) * 32);
423
424           if (!counting_distros)
425             {
426               counting_distros = true;
427               distro_counter = 50;
428             }
429
430           if (distro_counter <= 0)
431             plevel->change(x, y, TM_IA, tile->next_tile);
432
433           play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
434           player_status.score = player_status.score + SCORE_DISTRO;
435           player_status.distros++;
436         }
437       else if (!small)
438         {
439           /* Get rid of it: */
440           plevel->change(x, y, TM_IA, tile->next_tile);
441           
442           /* Replace it with broken bits: */
443           add_broken_brick(tile, 
444                                  ((int)(x + 1) / 32) * 32,
445                                  (int)(y / 32) * 32);
446           
447           /* Get some score: */
448           play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
449           player_status.score = player_status.score + SCORE_BRICK;
450         }
451     }
452 }
453
454 /* Empty a box: */
455 void
456 World::tryemptybox(float x, float y, Direction col_side)
457 {
458   Tile* tile = gettile(x,y);
459   if (!tile->fullbox)
460     return;
461
462   // according to the collision side, set the upgrade direction
463   if(col_side == LEFT)
464     col_side = RIGHT;
465   else
466     col_side = LEFT;
467
468   int posx = ((int)(x+1) / 32) * 32;
469   int posy = (int)(y/32) * 32 - 32;
470   switch(tile->data)
471     {
472     case 1: // Box with a distro!
473       add_bouncy_distro(posx, posy);
474       play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
475       player_status.score = player_status.score + SCORE_DISTRO;
476       player_status.distros++;
477       break;
478
479     case 2: // Add an upgrade!
480       if (tux.size == SMALL)     /* Tux is small, add mints! */
481         add_upgrade(posx, posy, col_side, UPGRADE_GROWUP);
482       else     /* Tux is big, add an iceflower: */
483         add_upgrade(posx, posy, col_side, UPGRADE_ICEFLOWER);
484       play_sound(sounds[SND_UPGRADE], SOUND_CENTER_SPEAKER);
485       break;
486
487     case 3: // Add a golden herring
488       add_upgrade(posx, posy, col_side, UPGRADE_HERRING);
489       break;
490
491     case 4: // Add a 1up extra
492       add_upgrade(posx, posy, col_side, UPGRADE_1UP);
493       break;
494     default:
495       break;
496     }
497
498   /* Empty the box: */
499   level->change(x, y, TM_IA, tile->next_tile);
500 }
501
502 /* Try to grab a distro: */
503 void
504 World::trygrabdistro(float x, float y, int bounciness)
505 {
506   Tile* tile = gettile(x, y);
507   if (tile && tile->distro)
508     {
509       level->change(x, y, TM_IA, tile->next_tile);
510       play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
511
512       if (bounciness == BOUNCE)
513         {
514           add_bouncy_distro(((int)(x + 1) / 32) * 32,
515                                   (int)(y / 32) * 32);
516         }
517
518       player_status.score = player_status.score + SCORE_DISTRO;
519       player_status.distros++;
520     }
521 }
522
523 /* Try to bump a bad guy from below: */
524 void
525 World::trybumpbadguy(float x, float y)
526 {
527   /* Bad guys: */
528   for (unsigned int i = 0; i < bad_guys.size(); i++)
529     {
530       if (bad_guys[i].base.x >= x - 32 && bad_guys[i].base.x <= x + 32 &&
531           bad_guys[i].base.y >= y - 16 && bad_guys[i].base.y <= y + 16)
532         {
533           bad_guys[i].collision(&tux, CO_PLAYER, COLLISION_BUMP);
534         }
535     }
536
537
538   /* Upgrades: */
539   for (unsigned int i = 0; i < upgrades.size(); i++)
540     {
541       if (upgrades[i].base.height == 32 &&
542           upgrades[i].base.x >= x - 32 && upgrades[i].base.x <= x + 32 &&
543           upgrades[i].base.y >= y - 16 && upgrades[i].base.y <= y + 16)
544         {
545           upgrades[i].base.xm = -upgrades[i].base.xm;
546           upgrades[i].base.ym = -8;
547           play_sound(sounds[SND_BUMP_UPGRADE], SOUND_CENTER_SPEAKER);
548         }
549     }
550 }
551
552 /* EOF */
553