- new extras
[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 texture_type 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 / 30;
132       texture_draw_part(&level->img_bkgd, s, 0,0,0,level->img_bkgd.w - s, level->img_bkgd.h);
133       texture_draw_part(&level->img_bkgd, 0, 0,screen->w - s ,0,s,level->img_bkgd.h);
134     }
135   else
136     {
137       drawgradient(level->bkgd_top_red, level->bkgd_top_green, level->bkgd_top_blue,
138                      level->bkgd_bottom_red, level->bkgd_bottom_green, level->bkgd_bottom_blue);
139     }
140     
141   /* Draw particle systems (background) */
142   std::vector<ParticleSystem*>::iterator p;
143   for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
144     {
145       (*p)->draw(scroll_x, 0, 0);
146     }
147
148   /* Draw background: */
149   for (y = 0; y < 15; ++y)
150     {
151       for (x = 0; x < 21; ++x)
152         {
153           Tile::draw(32*x - fmodf(scroll_x, 32), y * 32,
154                      level->bg_tiles[(int)y][(int)x + (int)(scroll_x / 32)]);
155         }
156     }
157
158   /* Draw interactive tiles: */
159   for (y = 0; y < 15; ++y)
160     {
161       for (x = 0; x < 21; ++x)
162         {
163           Tile::draw(32*x - fmodf(scroll_x, 32), y * 32,
164                      level->ia_tiles[(int)y][(int)x + (int)(scroll_x / 32)]);
165         }
166     }
167
168   /* (Bouncy bricks): */
169   for (unsigned int i = 0; i < bouncy_bricks.size(); ++i)
170     bouncy_bricks[i].draw();
171
172   for (unsigned int i = 0; i < bad_guys.size(); ++i)
173     bad_guys[i].draw();
174
175   tux.draw();
176
177   for (unsigned int i = 0; i < bullets.size(); ++i)
178     bullets[i].draw();
179
180   for (unsigned int i = 0; i < floating_scores.size(); ++i)
181     floating_scores[i].draw();
182
183   for (unsigned int i = 0; i < upgrades.size(); ++i)
184     upgrades[i].draw();
185
186   for (unsigned int i = 0; i < bouncy_distros.size(); ++i)
187     bouncy_distros[i].draw();
188
189   for (unsigned int i = 0; i < broken_bricks.size(); ++i)
190     broken_bricks[i].draw();
191
192   /* Draw foreground: */
193   for (y = 0; y < 15; ++y)
194     {
195       for (x = 0; x < 21; ++x)
196         {
197           Tile::draw(32*x - fmodf(scroll_x, 32), y * 32,
198                      level->fg_tiles[(int)y][(int)x + (int)(scroll_x / 32)]);
199         }
200     }
201
202   /* Draw particle systems (foreground) */
203   for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
204     {
205       (*p)->draw(scroll_x, 0, 1);
206     }
207 }
208
209 void
210 World::action(double frame_ratio)
211 {
212   /* Handle bouncy distros: */
213   for (unsigned int i = 0; i < bouncy_distros.size(); i++)
214     bouncy_distros[i].action(frame_ratio);
215
216   /* Handle broken bricks: */
217   for (unsigned int i = 0; i < broken_bricks.size(); i++)
218     broken_bricks[i].action(frame_ratio);
219
220   /* Handle distro counting: */
221   if (counting_distros)
222     {
223       distro_counter--;
224
225       if (distro_counter <= 0)
226         counting_distros = -1;
227     }
228
229   // Handle all kinds of game objects
230   for (unsigned int i = 0; i < bouncy_bricks.size(); i++)
231     bouncy_bricks[i].action(frame_ratio);
232   
233   for (unsigned int i = 0; i < floating_scores.size(); i++)
234     floating_scores[i].action(frame_ratio);
235
236   for (unsigned int i = 0; i < bullets.size(); ++i)
237     bullets[i].action(frame_ratio);
238   
239   for (unsigned int i = 0; i < upgrades.size(); i++)
240     upgrades[i].action(frame_ratio);
241
242   for (unsigned int i = 0; i < bad_guys.size(); i++)
243     bad_guys[i].action(frame_ratio);
244
245   /* update particle systems */
246   std::vector<ParticleSystem*>::iterator p;
247   for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
248     {
249       (*p)->simulate(frame_ratio);
250     }
251
252   /* Handle all possible collisions. */
253   collision_handler();
254 }
255
256
257 void
258 World::collision_handler()
259 {
260   // CO_BULLET & CO_BADGUY check
261   for(unsigned int i = 0; i < bullets.size(); ++i)
262     {
263       for(unsigned int j = 0; j < bad_guys.size(); ++j)
264         {
265           if(bad_guys[j].dying != DYING_NOT)
266             continue;
267           if(rectcollision(&bullets[i].base, &bad_guys[j].base))
268             {
269               // We have detected a collision and now call the
270               // collision functions of the collided objects.
271               // collide with bad_guy first, since bullet_collision will
272               // delete the bullet
273               bad_guys[j].collision(0, CO_BULLET);
274               bullets[i].collision(CO_BADGUY);
275               break; // bullet is invalid now, so break
276             }
277         }
278     }
279
280   /* CO_BADGUY & CO_BADGUY check */
281   for(unsigned int i = 0; i < bad_guys.size(); ++i)
282     {
283       if(bad_guys[i].dying != DYING_NOT)
284         continue;
285       
286       for(unsigned int j = i+1; j < bad_guys.size(); ++j)
287         {
288           if(j == i || bad_guys[j].dying != DYING_NOT)
289             continue;
290
291           if(rectcollision(&bad_guys[i].base, &bad_guys[j].base))
292             {
293               // We have detected a collision and now call the
294               // collision functions of the collided objects.
295               bad_guys[j].collision(&bad_guys[i], CO_BADGUY);
296               bad_guys[i].collision(&bad_guys[j], CO_BADGUY);
297             }
298         }
299     }
300
301   if(tux.dying != DYING_NOT) return;
302     
303   // CO_BADGUY & CO_PLAYER check 
304   for(unsigned int i = 0; i < bad_guys.size(); ++i)
305     {
306       if(bad_guys[i].dying != DYING_NOT)
307         continue;
308       
309       if(rectcollision_offset(&bad_guys[i].base,&tux.base,0,0))
310         {
311           // We have detected a collision and now call the collision
312           // functions of the collided objects.
313           if (tux.previous_base.y < tux.base.y &&
314               tux.previous_base.y + tux.previous_base.height 
315               < bad_guys[i].base.y + bad_guys[i].base.height/2)
316             {
317               bad_guys[i].collision(&tux, CO_PLAYER, COLLISION_SQUISH);
318             }
319           else
320             {
321               tux.collision(&bad_guys[i], CO_BADGUY);
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)
384 {
385   bad_guys.push_back(BadGuy());
386   BadGuy& new_bad_guy = bad_guys.back();
387   
388   new_bad_guy.init(x,y,kind);
389 }
390
391 void
392 World::add_upgrade(float x, float y, int dir, int 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, int 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, int 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   switch(tile->data)
469     {
470     case 1: // Box with a distro!
471       add_bouncy_distro(((int)(x + 1) / 32) * 32, (int)(y / 32) * 32 - 32);
472       play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
473       player_status.score = player_status.score + SCORE_DISTRO;
474       player_status.distros++;
475       break;
476
477     case 2: // Add an upgrade!
478       if (tux.size == SMALL)     /* Tux is small, add mints! */
479         add_upgrade((int)((x + 1) / 32) * 32, (int)(y / 32) * 32 - 32, col_side, UPGRADE_MINTS);
480       else     /* Tux is big, add coffee: */
481         add_upgrade((int)((x + 1) / 32) * 32, (int)(y / 32) * 32 - 32, col_side, UPGRADE_COFFEE);
482       play_sound(sounds[SND_UPGRADE], SOUND_CENTER_SPEAKER);
483       break;
484
485     case 3: // Add a golden herring
486       add_upgrade((int)((x + 1) / 32) * 32, (int)(y / 32) * 32 - 32, col_side, UPGRADE_HERRING);
487       break;
488     default:
489       break;
490     }
491
492   /* Empty the box: */
493   level->change(x, y, TM_IA, tile->next_tile);
494 }
495
496 /* Try to grab a distro: */
497 void
498 World::trygrabdistro(float x, float y, int bounciness)
499 {
500   Tile* tile = gettile(x, y);
501   if (tile && tile->distro)
502     {
503       level->change(x, y, TM_IA, tile->next_tile);
504       play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
505
506       if (bounciness == BOUNCE)
507         {
508           add_bouncy_distro(((int)(x + 1) / 32) * 32,
509                                   (int)(y / 32) * 32);
510         }
511
512       player_status.score = player_status.score + SCORE_DISTRO;
513       player_status.distros++;
514     }
515 }
516
517 /* Try to bump a bad guy from below: */
518 void
519 World::trybumpbadguy(float x, float y)
520 {
521   /* Bad guys: */
522   for (unsigned int i = 0; i < bad_guys.size(); i++)
523     {
524       if (bad_guys[i].base.x >= x - 32 && bad_guys[i].base.x <= x + 32 &&
525           bad_guys[i].base.y >= y - 16 && bad_guys[i].base.y <= y + 16)
526         {
527           bad_guys[i].collision(&tux, CO_PLAYER, COLLISION_BUMP);
528         }
529     }
530
531
532   /* Upgrades: */
533   for (unsigned int i = 0; i < upgrades.size(); i++)
534     {
535       if (upgrades[i].base.height == 32 &&
536           upgrades[i].base.x >= x - 32 && upgrades[i].base.x <= x + 32 &&
537           upgrades[i].base.y >= y - 16 && upgrades[i].base.y <= y + 16)
538         {
539           upgrades[i].base.xm = -upgrades[i].base.xm;
540           upgrades[i].base.ym = -8;
541           play_sound(sounds[SND_BUMP_UPGRADE], SOUND_CENTER_SPEAKER);
542         }
543     }
544 }
545
546 /* EOF */
547