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