merged bad_guy patch from Matze Braun. (recycling Ricardo's stalactite patch and...
[supertux.git] / src / gameloop.cpp
index 9dd970c..4a8d7f2 100644 (file)
@@ -16,6 +16,7 @@
 #include <string.h>
 #include <errno.h>
 #include <unistd.h>
+#include <math.h>
 #include <time.h>
 #include <SDL.h>
 
@@ -38,6 +39,8 @@
 #include "level.h"
 #include "scene.h"
 #include "collision.h"
+#include "tile.h"
+#include "particlesystem.h"
 
 /* extern variables */
 
@@ -97,22 +100,28 @@ void start_timers(void)
 
 void activate_bad_guys(void)
 {
-  int x,y;
-
-  /* Activate bad guys: */
-
-  for (y = 0; y < 15; y++)
+  for (std::vector<BadGuyData>::iterator i = current_level.badguy_data.begin();
+       i != current_level.badguy_data.end();
+       ++i)
     {
-      for (x = 0; x < current_level.width; x++)
-        {
-          if (current_level.tiles[y][x] >= '0' && current_level.tiles[y][x] <= '9')
-            {
-              add_bad_guy(x * 32, y * 32, static_cast<BadGuyKind>(current_level.tiles[y][x] - '0'));
-              current_level.tiles[y][x] = '.';
-            }
-        }
+      add_bad_guy(i->x, i->y, i->kind);
     }
+}
 
+void activate_particle_systems(void)
+{
+  if(current_level.particle_system == "clouds")
+    {
+      particle_systems.push_back(new CloudParticleSystem);
+    }
+  else if(current_level.particle_system == "snow")
+    {
+      particle_systems.push_back(new SnowParticleSystem);
+    }
+  else if(current_level.particle_system != "")
+    {
+      st_abort("unknown particle system specified in level", "");
+    }
 }
 
 /* --- GAME EVENT! --- */
@@ -222,9 +231,9 @@ void game_event(void)
             case SDLK_f:
               if(debug_fps)
                 debug_fps = false;
-             else
+              else
                 debug_fps = true;
-              break;         
+              break;
             default:
               break;
             }
@@ -236,32 +245,32 @@ void game_event(void)
             case JOY_X:
               if (event.jaxis.value < -JOYSTICK_DEAD_ZONE)
                 {
-                  tux.input_.left  = DOWN;
-                  tux.input_.right = UP;
+                  tux.input.left  = DOWN;
+                  tux.input.right = UP;
                 }
               else if (event.jaxis.value > JOYSTICK_DEAD_ZONE)
                 {
-                  tux.input_.left  = UP;
-                  tux.input_.right = DOWN;
+                  tux.input.left  = UP;
+                  tux.input.right = DOWN;
                 }
               else
                 {
-                  tux.input_.left  = DOWN;
-                  tux.input_.right = DOWN;
+                  tux.input.left  = DOWN;
+                  tux.input.right = DOWN;
                 }
               break;
             case JOY_Y:
               if (event.jaxis.value > JOYSTICK_DEAD_ZONE)
-                tux.input_.down = DOWN;
+                tux.input.down = DOWN;
               else if (event.jaxis.value < -JOYSTICK_DEAD_ZONE)
-                tux.input_.down = UP;
+                tux.input.down = UP;
               else
-                tux.input_.down = UP;
+                tux.input.down = UP;
 
               /* Handle joystick for the menu */
               if(show_menu)
                 {
-                  if(tux.input_.down == DOWN)
+                  if(tux.input.down == DOWN)
                     menuaction = MENU_ACTION_DOWN;
                   else
                     menuaction = MENU_ACTION_UP;
@@ -273,15 +282,15 @@ void game_event(void)
           break;
         case SDL_JOYBUTTONDOWN:
           if (event.jbutton.button == JOY_A)
-            tux.input_.up = DOWN;
+            tux.input.up = DOWN;
           else if (event.jbutton.button == JOY_B)
-            tux.input_.fire = DOWN;
+            tux.input.fire = DOWN;
           break;
         case SDL_JOYBUTTONUP:
           if (event.jbutton.button == JOY_A)
-            tux.input_.up = UP;
+            tux.input.up = UP;
           else if (event.jbutton.button == JOY_B)
-            tux.input_.fire = UP;
+            tux.input.fire = UP;
 
           if(show_menu)
             menuaction = MENU_ACTION_HIT;
@@ -374,6 +383,7 @@ int game_action(void)
       arrays_free();
       arrays_init();
       activate_bad_guys();
+      activate_particle_systems();
       level_free_gfx();
       level_load_gfx(&current_level);
       level_free_song();
@@ -450,6 +460,13 @@ int game_action(void)
       bad_guys[i].action();
     }
 
+  /* update particle systems */
+  std::vector<ParticleSystem*>::iterator p;
+  for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
+    {
+      (*p)->simulate(frame_ratio);
+    }
+
   /* Handle all possible collisions. */
   collision_handler();
 
@@ -460,6 +477,8 @@ int game_action(void)
 
 void game_draw(void)
 {
+int y,x;
+
   /* Draw screen: */
   if (tux.dying && (global_frame_counter % 4) == 0)
     clearscreen(255, 255, 255);
@@ -480,11 +499,36 @@ void game_draw(void)
         }
     }
 
-  // Draw background:
-  for (int y = 0; y < 15; ++y)
-    for (int x = 0; x < 21; ++x)
-      drawshape(32*x - fmodf(scroll_x, 32), y * 32,
-                current_level.tiles[y][x + (int)(scroll_x / 32)]);
+  /* Draw particle systems (background) */
+  std::vector<ParticleSystem*>::iterator p;
+  for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
+    {
+      (*p)->draw(scroll_x, 0, 0);
+    }
+
+  /* Draw background: */
+
+  for (y = 0; y < 15; ++y)
+    {
+      for (x = 0; x < 21; ++x)
+        {
+          drawshape(32*x - fmodf(scroll_x, 32), y * 32,
+                    current_level.bg_tiles[(int)y][(int)x + (int)(scroll_x / 32)]);
+        }
+    }
+
+  /* Draw interactive tiles: */
+
+  for (y = 0; y < 15; ++y)
+    {
+      for (x = 0; x < 21; ++x)
+        {
+          drawshape(32*x - fmodf(scroll_x, 32), y * 32,
+                    current_level.ia_tiles[(int)y][(int)x + (int)(scroll_x / 32)]);
+        }
+    }
+
+  /* (Bouncy bricks): */
 
   for (unsigned int i = 0; i < bouncy_bricks.size(); ++i)
     bouncy_brick_draw(&bouncy_bricks[i]);
@@ -509,6 +553,23 @@ void game_draw(void)
   for (unsigned int i = 0; i < broken_bricks.size(); ++i)
     broken_brick_draw(&broken_bricks[i]);
 
+  /* Draw foreground: */
+
+  for (y = 0; y < 15; ++y)
+    {
+      for (x = 0; x < 21; ++x)
+        {
+          drawshape(32*x - fmodf(scroll_x, 32), y * 32,
+                    current_level.fg_tiles[(int)y][(int)x + (int)(scroll_x / 32)]);
+        }
+    }
+
+  /* Draw particle systems (foreground) */
+  for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
+    {
+      (*p)->draw(scroll_x, 0, 1);
+    }
+
   drawstatus();
 
   if(game_pause)
@@ -526,10 +587,7 @@ void game_draw(void)
     menu_process_current();
 
   /* (Update it all!) */
-
   updatescreen();
-
-
 }
 
 /* --- GAME LOOP! --- */
@@ -565,6 +623,7 @@ int gameloop(const char * subset, int levelnb, int mode)
 
   level_load_gfx(&current_level);
   activate_bad_guys();
+  activate_particle_systems();
   level_load_song(&current_level);
 
   tux.init();
@@ -583,7 +642,6 @@ int gameloop(const char * subset, int levelnb, int mode)
   if(st_gl_mode == ST_GL_LOAD_GAME)
     loadgame(levelnb);
 
-
   /* --- MAIN GAME LOOP!!! --- */
 
   jump = false;
@@ -605,7 +663,7 @@ int gameloop(const char * subset, int levelnb, int mode)
 
 
   while (SDL_PollEvent(&event))
-    {}
+  {}
 
   game_draw();
   do
@@ -616,7 +674,7 @@ int gameloop(const char * subset, int levelnb, int mode)
       frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
       if(frame_ratio > 1.5) /* Quick hack to correct the unprecise CPU clocks a little bit. */
         frame_ratio = 1.5 + (frame_ratio - 1.5) * 0.85;
-      
+
       if(!timer_check(&frame_timer))
         {
           timer_start(&frame_timer,25);
@@ -625,7 +683,7 @@ int gameloop(const char * subset, int levelnb, int mode)
 
       /* Handle events: */
 
-      tux.input_.old_fire = tux.input_.fire;
+      tux.input.old_fire = tux.input.fire;
 
       game_event();
 
@@ -679,8 +737,8 @@ int gameloop(const char * subset, int levelnb, int mode)
               /* == -1: continues */
               return 0;
             }
-         /*  --z;
-              }*/
+          /*  --z;
+                     }*/
         }
       else
         {
@@ -711,7 +769,6 @@ int gameloop(const char * subset, int levelnb, int mode)
       last_update_time = update_time;
       update_time = st_get_ticks();
 
-
       /* Pause till next frame, if the machine running the game is too fast: */
       /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
          the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
@@ -1087,7 +1144,21 @@ void loadshared(void)
                "/images/shared/bag-right-1.png",
                USE_ALPHA);
 
+  /* Mr. Bomb */
+  for(int i=0; i<4; ++i) {
+      char num[4];
+      snprintf(num, 4, "%d", i);
+      texture_load(&img_mrbomb_left[i],
+              datadir + "/images/shared/mrbomb-left-" + num + ".png", USE_ALPHA);
+      texture_load(&img_mrbomb_right[i],
+              datadir + "/images/shared/mrbomb-right-" + num + ".png", USE_ALPHA);
+  }
 
+  /* stalactite */
+  texture_load(&img_stalactite, 
+          datadir + "/images/shared/stalactite.png", USE_ALPHA);
+  texture_load(&img_stalactite_broken,
+          datadir + "/images/shared/stalactite-broken.png", USE_ALPHA);
 
   /* Upgrades: */
 
@@ -1214,6 +1285,14 @@ void unloadshared(void)
       texture_free(&img_money_right[i]);
     }
 
+  for(i = 0; i < 4; i++) {
+      texture_free(&img_mrbomb_left[i]);
+      texture_free(&img_mrbomb_right[i]);
+  }
+
+  texture_free(&img_stalactite);
+  texture_free(&img_stalactite_broken);
+
   texture_free(&img_box_full);
   texture_free(&img_box_empty);
 
@@ -1249,10 +1328,29 @@ void unloadshared(void)
 
 /* Draw a tile on the screen: */
 
-void drawshape(float x, float y, unsigned char c)
+void drawshape(float x, float y, unsigned int c)
 {
-  int z;
+  if (c != 0)
+    {
+      Tile* ptile = TileManager::instance()->get(c);
+      if(ptile)
+        {
+          if(ptile->images.size() > 1)
+            {
+              texture_draw(&ptile->images[( ((global_frame_counter*25) / ptile->anim_speed) % (ptile->images.size()))],x,y);
+            }
+          else if (ptile->images.size() == 1)
+            {
+              texture_draw(&ptile->images[0],x,y);
+            }
+          else
+            {
+              printf("Tile not dravable %u\n", c);
+            }
+        }
+    }
 
+  /*
   if (c == 'X' || c == 'x')
     texture_draw(&img_brick[0], x, y);
   else if (c == 'Y' || c == 'y')
@@ -1308,55 +1406,70 @@ void drawshape(float x, float y, unsigned char c)
       texture_draw(&img_flag[z], x + 16, y);
     }
   else if (c == '&')
-    texture_draw(&img_water, x, y);
+    texture_draw(&img_water, x, y);*/
+
 }
 
 
 /* What shape is at some position? */
-
-unsigned char shape(float x, float y)
+unsigned int shape(float x, float y)
 {
 
   int xx, yy;
-  unsigned char c;
+  unsigned int c;
 
   yy = ((int)y / 32);
   xx = ((int)x / 32);
 
   if (yy >= 0 && yy < 15 && xx >= 0 && xx <= current_level.width)
     {
-      c = current_level.tiles[yy][xx];
+      c = current_level.ia_tiles[yy][xx];
     }
   else
-    c = '.';
+    c = 0;
 
   return(c);
 }
 
-/* Is is ground? */
-
+Tile* gettile(float x, float y)
+{
+  return TileManager::instance()->get(shape(x, y));
+}
 
 bool issolid(float x, float y)
 {
-  return (isbrick(x, y) ||
-          isice(x, y) ||
-          (shape(x, y) == '[') ||
-          (shape(x, y) == '=') ||
-          (shape(x, y) == ']') ||
-          (shape(x, y) == 'A') ||
-          (shape(x, y) == 'B') ||
-          (shape(x, y) == '!') ||
-          (shape(x, y) == 'a'));
+  Tile* tile = TileManager::instance()->get
+               (shape(x,y));
+  if(tile)
+    {
+      if(tile->solid == true)
+        return true;
+      else
+        return false;
+    }
+  else
+    {
+      return false;
+    }
 }
 
 /* Is it a brick? */
 
 bool isbrick(float x, float y)
 {
-  return (shape(x, y) == 'X' ||
-          shape(x, y) == 'x' ||
-          shape(x, y) == 'Y' ||
-          shape(x, y) == 'y');
+  Tile* tile = TileManager::instance()->get
+               (shape(x,y));
+  if(tile)
+    {
+      if(tile->brick == true)
+        return true;
+      else
+        return false;
+    }
+  else
+    {
+      return false;
+    }
 }
 
 
@@ -1364,28 +1477,56 @@ bool isbrick(float x, float y)
 
 bool isice(float x, float y)
 {
-  return (shape(x, y) == '#');
+  Tile* tile = TileManager::instance()->get
+               (shape(x,y));
+  if(tile)
+    {
+      if(tile->ice == true)
+        return true;
+      else
+        return false;
+    }
+  else
+    {
+      return false;
+    }
 }
 
 /* Is it a full box? */
 
 bool isfullbox(float x, float y)
 {
-  return (shape(x, y) == 'A' ||
-          shape(x, y) == 'B' ||
-          shape(x, y) == '!');
+  Tile* tile = TileManager::instance()->get
+               (shape(x,y));
+  if(tile)
+    {
+      if(tile->fullbox == true)
+        return true;
+      else
+        return false;
+    }
+  else
+    {
+      return false;
+    }
+}
+
+bool isdistro(float x, float y)
+{
+  Tile* tile = TileManager::instance()->get(shape(x,y));
+  return tile && tile->distro;
 }
 
 /* Break a brick: */
 
 void trybreakbrick(float x, float y)
 {
-  if (isbrick(x, y))
+  Tile* tile = gettile(x, y);
+  if (tile->brick)
     {
-      if (shape(x, y) == 'x' || shape(x, y) == 'y')
+      if (tile->data > 0)
         {
           /* Get a distro from it: */
-
           add_bouncy_distro(((int)(x + 1) / 32) * 32,
                             (int)(y / 32) * 32);
 
@@ -1396,7 +1537,7 @@ void trybreakbrick(float x, float y)
             }
 
           if (distro_counter <= 0)
-            level_change(&current_level,x, y, 'a');
+            level_change(&current_level,x, y, TM_IA, tile->next_tile2);
 
           play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
           score = score + SCORE_DISTRO;
@@ -1405,8 +1546,7 @@ void trybreakbrick(float x, float y)
       else
         {
           /* Get rid of it: */
-
-          level_change(&current_level,x, y,'.');
+          level_change(&current_level,x, y, TM_IA, tile->next_tile);
         }
 
 
@@ -1437,35 +1577,36 @@ void bumpbrick(float x, float y)
 
 
 /* Empty a box: */
-
 void tryemptybox(float x, float y, int col_side)
 {
-  if (!isfullbox(x, y))
+  Tile* tile = gettile(x,y);
+  if (!tile->fullbox)
     return;
 
   // according to the collision side, set the upgrade direction
-
   if(col_side == LEFT)
     col_side = RIGHT;
   else
     col_side = LEFT;
 
-  switch(shape(x,y))
+  switch(tile->data)
     {
-    case 'A':      /* Box with a distro! */
+    case 1: //'A':      /* Box with a distro! */
       add_bouncy_distro(((int)(x + 1) / 32) * 32, (int)(y / 32) * 32 - 32);
       play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
       score = score + SCORE_DISTRO;
       distros++;
       break;
-    case 'B':      /* Add an upgrade! */
+
+    case 2: // 'B':      /* Add an upgrade! */
       if (tux.size == SMALL)     /* Tux is small, add mints! */
         add_upgrade((int)((x + 1) / 32) * 32, (int)(y / 32) * 32 - 32, col_side, UPGRADE_MINTS);
       else     /* Tux is big, add coffee: */
         add_upgrade((int)((x + 1) / 32) * 32, (int)(y / 32) * 32 - 32, col_side, UPGRADE_COFFEE);
       play_sound(sounds[SND_UPGRADE], SOUND_CENTER_SPEAKER);
       break;
-    case '!':     /* Add a golden herring */
+
+    case 3:// '!':     /* Add a golden herring */
       add_upgrade((int)((x + 1) / 32) * 32, (int)(y / 32) * 32 - 32, col_side, UPGRADE_HERRING);
       break;
     default:
@@ -1473,17 +1614,16 @@ void tryemptybox(float x, float y, int col_side)
     }
 
   /* Empty the box: */
-  level_change(&current_level,x, y, 'a');
+  level_change(&current_level,x, y, TM_IA, tile->next_tile);
 }
 
-
 /* Try to grab a distro: */
-
 void trygrabdistro(float x, float y, int bounciness)
 {
-  if (shape(x, y) == '$')
+  Tile* tile = gettile(x, y);
+  if (tile && tile->distro)
     {
-      level_change(&current_level,x, y, '.');
+      level_change(&current_level,x, y, TM_IA, tile->next_tile);
       play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
 
       if (bounciness == BOUNCE)
@@ -1498,30 +1638,21 @@ void trygrabdistro(float x, float y, int bounciness)
 }
 
 /* Try to bump a bad guy from below: */
-
 void trybumpbadguy(float x, float y)
 {
-  unsigned int i;
-
   /* Bad guys: */
-  for (i = 0; i < bad_guys.size(); i++)
+  for (unsigned int i = 0; i < bad_guys.size(); i++)
     {
       if (bad_guys[i].base.x >= x - 32 && bad_guys[i].base.x <= x + 32 &&
           bad_guys[i].base.y >= y - 16 && bad_guys[i].base.y <= y + 16)
         {
-          if (bad_guys[i].kind == BAD_BSOD ||
-              bad_guys[i].kind == BAD_LAPTOP)
-            {
-              bad_guys[i].dying = DYING_FALLING;
-              bad_guys[i].base.ym = -8;
-              play_sound(sounds[SND_FALL], SOUND_CENTER_SPEAKER);
-            }
+          bad_guys[i].collision(&tux, CO_PLAYER, COLLISION_BUMP);
         }
     }
 
 
   /* Upgrades: */
-  for (i = 0; i < upgrades.size(); i++)
+  for (unsigned int i = 0; i < upgrades.size(); i++)
     {
       if (upgrades[i].base.height == 32 &&
           upgrades[i].base.x >= x - 32 && upgrades[i].base.x <= x + 32 &&
@@ -1630,7 +1761,6 @@ void savegame(int slot)
   if (fi == NULL)
     {
       fprintf(stderr, "Warning: I could not open the slot file ");
-
     }
   else
     {
@@ -1683,6 +1813,7 @@ void loadgame(int slot)
       arrays_free();
       arrays_init();
       activate_bad_guys();
+      activate_particle_systems();
       level_free_gfx();
       level_load_gfx(&current_level);
       level_free_song();