We have our own mouse-cursor now! (graphics by Settra Gaia)
[supertux.git] / src / gameloop.cpp
index d5097b3..d59de79 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! --- */
@@ -121,6 +130,9 @@ void game_event(void)
 {
   while (SDL_PollEvent(&event))
     {
+          /* Check for menu-events, if the menu is shown */
+          if(show_menu)
+            menu_event(event);
       switch(event.type)
         {
         case SDL_QUIT:        /* Quit event - quit: */
@@ -129,10 +141,6 @@ void game_event(void)
         case SDL_KEYDOWN:     /* A keypress! */
           key = event.key.keysym.sym;
 
-          /* Check for menu-events, if the menu is shown */
-          if(show_menu)
-            menu_event(&event.key.keysym);
-
           if(tux.key_event(key,DOWN))
             break;
 
@@ -222,9 +230,9 @@ void game_event(void)
             case SDLK_f:
               if(debug_fps)
                 debug_fps = false;
-             else
+              else
                 debug_fps = true;
-              break;         
+              break;
             default:
               break;
             }
@@ -257,16 +265,8 @@ void game_event(void)
                 tux.input.down = UP;
               else
                 tux.input.down = UP;
-
-              /* Handle joystick for the menu */
-              if(show_menu)
-                {
-                  if(tux.input.down == DOWN)
-                    menuaction = MENU_ACTION_DOWN;
-                  else
-                    menuaction = MENU_ACTION_UP;
-                }
-              break;
+              
+             break;
             default:
               break;
             }
@@ -282,9 +282,7 @@ void game_event(void)
             tux.input.up = UP;
           else if (event.jbutton.button == JOY_B)
             tux.input.fire = UP;
-
-          if(show_menu)
-            menuaction = MENU_ACTION_HIT;
+           
           break;
 
         default:
@@ -374,6 +372,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 +449,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 +466,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,12 +488,35 @@ 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]);
 
@@ -504,11 +535,27 @@ void game_draw(void)
     upgrade_draw(&upgrades[i]);
 
   for (unsigned int i = 0; i < bouncy_distros.size(); ++i)
-      bouncy_distro_draw(&bouncy_distros[i]);
+    bouncy_distro_draw(&bouncy_distros[i]);
 
   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)
@@ -523,13 +570,13 @@ void game_draw(void)
     }
 
   if(show_menu)
+  {
     menu_process_current();
+    mouse_cursor->draw();
+  }
 
   /* (Update it all!) */
-
   updatescreen();
-
-
 }
 
 /* --- GAME LOOP! --- */
@@ -565,6 +612,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 +631,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 +652,7 @@ int gameloop(const char * subset, int levelnb, int mode)
 
 
   while (SDL_PollEvent(&event))
-    {}
+  {}
 
   game_draw();
   do
@@ -616,7 +663,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);
@@ -679,8 +726,8 @@ int gameloop(const char * subset, int levelnb, int mode)
               /* == -1: continues */
               return 0;
             }
-         /*  --z;
-              }*/
+          /*  --z;
+                     }*/
         }
       else
         {
@@ -711,7 +758,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 +1133,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 +1274,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 +1317,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 +1395,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 +1466,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 +1526,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 +1535,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 +1566,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 +1603,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 +1627,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 +1750,6 @@ void savegame(int slot)
   if (fi == NULL)
     {
       fprintf(stderr, "Warning: I could not open the slot file ");
-
     }
   else
     {
@@ -1683,6 +1802,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();