rnd-20030428-3-src
[rocksndiamonds.git] / src / game.c
index 0f4ce58eb9455bbb3bcd9d0231a783da8a3ad5dd..6674016f718bb97cda0bff364d4a389852842f23 100644 (file)
@@ -14,9 +14,9 @@
 #include "libgame/libgame.h"
 
 #include "game.h"
+#include "init.h"
 #include "tools.h"
 #include "screens.h"
-#include "init.h"
 #include "files.h"
 #include "tape.h"
 #include "network.h"
@@ -500,9 +500,13 @@ static void InitGameEngine()
 {
   int i;
 
+  /* set game engine from tape file when re-playing, else from level file */
   game.engine_version = (tape.playing ? tape.engine_version :
                         level.game_version);
 
+  /* dynamically adjust element properties according to game engine version */
+  InitElementPropertiesEngine(game.engine_version);
+
 #if 0
     printf("level %d: level version == %06d\n", level_nr, level.game_version);
     printf("          tape version == %06d [%s] [file: %06d]\n",
@@ -520,6 +524,7 @@ static void InitGameEngine()
   game.initial_move_delay_value =
     (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
 
+#if 0
   /* dynamically adjust element properties according to game engine version */
   {
     static int ep_em_slippery_wall[] =
@@ -543,6 +548,7 @@ static void InitGameEngine()
                 (level.em_slippery_gems &&
                  game.engine_version > VERSION_IDENT(2,0,1)));
   }
+#endif
 
   /* initialize changing elements information */
   for (i=0; i<MAX_NUM_ELEMENTS; i++)
@@ -941,7 +947,7 @@ void InitGame()
   if (setup.sound_music)
     PlayMusic(level_nr);
 
-  KeyboardAutoRepeatOff();
+  KeyboardAutoRepeatOffUnlessAutoplay();
 
   if (options.debug)
   {
@@ -1176,7 +1182,7 @@ void GameWon()
 
   if ((hi_pos = NewHiScore()) >= 0) 
   {
-    game_status = HALLOFFAME;
+    game_status = GAME_MODE_SCORES;
     DrawHallOfFame(hi_pos);
     if (raise_level)
     {
@@ -1186,7 +1192,7 @@ void GameWon()
   }
   else
   {
-    game_status = MAINMENU;
+    game_status = GAME_MODE_MAIN;
     if (raise_level)
     {
       level_nr++;
@@ -1535,14 +1541,14 @@ void Explode(int ex, int ey, int phase, int mode)
       }
 
 #if 1
+      if (IS_EXPLOSION_PROOF(element))
+       continue;
+#else
       if ((IS_INDESTRUCTIBLE(element) &&
           (game.engine_version < VERSION_IDENT(2,2,0) ||
            (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
          element == EL_FLAMES)
        continue;
-#else
-      if (IS_INDESTRUCTIBLE(element) || element == EL_FLAMES)
-        continue;
 #endif
 
       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
@@ -1557,26 +1563,14 @@ void Explode(int ex, int ey, int phase, int mode)
        continue;
       }
 
-#if 1
       /* save walkable background elements while explosion on same tile */
       if (IS_INDESTRUCTIBLE(element))
        Back[x][y] = element;
-#endif
 
       /* ignite explodable elements reached by other explosion */
       if (element == EL_EXPLOSION)
        element = Store2[x][y];
 
-#if 0
-      else if (IS_INDESTRUCTIBLE(Store2[x][y]))        /* hard element under bomb */
-       element = Store2[x][y];
-#endif
-
-#if 0
-      else if (IS_INDESTRUCTIBLE(Store[x][y])) /* hard element under bomb */
-       element = Store[x][y];
-#endif
-
       if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
       {
        switch(StorePlayer[ex][ey])
@@ -1599,18 +1593,6 @@ void Explode(int ex, int ey, int phase, int mode)
        if (game.emulation == EMU_SUPAPLEX)
          Store[x][y] = EL_EMPTY;
       }
-#if 0
-      else if (IS_INDESTRUCTIBLE(Store[x][y]))
-       ;
-#endif
-#if 0
-      else if (IS_INDESTRUCTIBLE(element))
-       Store[x][y] = element;
-#endif
-#if 0
-      else if (IS_INDESTRUCTIBLE(element) && IS_ACCESSIBLE(element))
-       Store[x][y] = element;
-#endif
       else if (center_element == EL_MOLE)
        Store[x][y] = EL_EMERALD_RED;
       else if (center_element == EL_PENGUIN)
@@ -1641,31 +1623,8 @@ void Explode(int ex, int ey, int phase, int mode)
        Store[x][y] = EL_PEARL;
       else if (element == EL_WALL_CRYSTAL)
        Store[x][y] = EL_CRYSTAL;
-#if 1
-#if 0
-#if 0
-      else if (IS_INDESTRUCTIBLE(element) && IS_ACCESSIBLE(element))
-       Store[x][y] = element;
-#else
-      else if (IS_INDESTRUCTIBLE(element))
-       Store[x][y] = element;
-#endif
-#endif
-      else
-       Store[x][y] = EL_EMPTY;
-#else
-
-#if 0
-      else if (IS_PFORTE(element))
-       Store[x][y] = element;
       else
        Store[x][y] = EL_EMPTY;
-#else
-      else if (!IS_PFORTE(Store[x][y]))
-       Store[x][y] = EL_EMPTY;
-#endif
-
-#endif
 
       if (x != ex || y != ey ||
          center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
@@ -1719,7 +1678,7 @@ void Explode(int ex, int ey, int phase, int mode)
 
     if (IS_PLAYER(x, y))
       KillHeroUnlessProtected(x, y);
-    else if (IS_EXPLOSIVE(element))
+    else if (IS_CAN_EXPLODE(element))
     {
       Feld[x][y] = Store2[x][y];
       Store2[x][y] = 0;
@@ -1736,15 +1695,13 @@ void Explode(int ex, int ey, int phase, int mode)
     element = Feld[x][y] = Store[x][y];
     Store[x][y] = Store2[x][y] = 0;
 
-#if 1
     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
       element = Feld[x][y] = Back[x][y];
     Back[x][y] = 0;
-#endif
 
     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
     InitField(x, y, FALSE);
-    if (CAN_MOVE(element) || COULD_MOVE(element))
+    if (CAN_MOVE(element))
       InitMovDir(x, y);
     DrawLevelField(x, y);
 
@@ -1762,7 +1719,6 @@ void Explode(int ex, int ey, int phase, int mode)
     if (phase == delay)
       DrawLevelFieldCrumbledSand(x, y);
 
-#if 1
     if (IS_WALKABLE_OVER(Back[x][y]))
     {
       DrawLevelElement(x, y, Back[x][y]);
@@ -1775,15 +1731,6 @@ void Explode(int ex, int ey, int phase, int mode)
     }
     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
-#else
-    if (IS_PFORTE(Store[x][y]))
-    {
-      DrawLevelElement(x, y, Store[x][y]);
-      DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
-    }
-    else
-      DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
-#endif
   }
 }
 
@@ -2282,7 +2229,7 @@ void Impact(int x, int y)
 
   if (!lastline && object_hit)         /* check which object was hit */
   {
-    if (CAN_CHANGE(element) && 
+    if (CAN_PASS_MAGIC_WALL(element) && 
        (smashed == EL_MAGIC_WALL ||
         smashed == EL_BD_MAGIC_WALL))
     {
@@ -2571,13 +2518,13 @@ void TurnRound(int x, int y)
     int rnd = RND(rnd_value);
 
     if (IN_LEV_FIELD(left_x, left_y) &&
-       (IS_FREE(left_x, left_y) || IS_GEM(Feld[left_x][left_y])))
+       (IS_FREE(left_x, left_y) || IS_FOOD_PIG(Feld[left_x][left_y])))
       can_turn_left = TRUE;
     if (IN_LEV_FIELD(right_x, right_y) &&
-       (IS_FREE(right_x, right_y) || IS_GEM(Feld[right_x][right_y])))
+       (IS_FREE(right_x, right_y) || IS_FOOD_PIG(Feld[right_x][right_y])))
       can_turn_right = TRUE;
     if (IN_LEV_FIELD(move_x, move_y) &&
-       (IS_FREE(move_x, move_y) || IS_GEM(Feld[move_x][move_y])))
+       (IS_FREE(move_x, move_y) || IS_FOOD_PIG(Feld[move_x][move_y])))
       can_move_on = TRUE;
 
     if (can_turn_left &&
@@ -2629,7 +2576,7 @@ void TurnRound(int x, int y)
       MovDir[x][y] = back_dir;
 
     if (!IS_FREE(x+move_xy[MovDir[x][y]].x, y+move_xy[MovDir[x][y]].y) &&
-       !IS_GEM(Feld[x+move_xy[MovDir[x][y]].x][y+move_xy[MovDir[x][y]].y]))
+       !IS_FOOD_PIG(Feld[x+move_xy[MovDir[x][y]].x][y+move_xy[MovDir[x][y]].y]))
       MovDir[x][y] = old_move_dir;
 
     MovDelay[x][y] = 0;
@@ -2856,7 +2803,7 @@ static boolean JustBeingPushed(int x, int y)
 
 void StartMoving(int x, int y)
 {
-  static boolean use_spring_bug = TRUE;
+  boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0));
   boolean started_moving = FALSE;      /* some elements can fall _and_ move */
   int element = Feld[x][y];
 
@@ -2979,7 +2926,7 @@ void StartMoving(int x, int y)
        Store[x][y] = 0;
       }
     }
-    else if (CAN_CHANGE(element) &&
+    else if (CAN_PASS_MAGIC_WALL(element) &&
             (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE ||
              Feld[x][y+1] == EL_BD_MAGIC_WALL_ACTIVE))
     {
@@ -3162,15 +3109,14 @@ void StartMoving(int x, int y)
          int sx = SCREENX(xx), sy = SCREENY(yy);
          int flame_graphic = graphic + (i - 1);
 
-         if (!IN_LEV_FIELD(xx, yy) ||
-             IS_SOLID(Feld[xx][yy]) || Feld[xx][yy] == EL_EXPLOSION)
+         if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
            break;
 
          if (MovDelay[x][y])
          {
            int flamed = MovingOrBlocked2Element(xx, yy);
 
-           if (IS_ENEMY(flamed) || IS_EXPLOSIVE(flamed))
+           if (IS_ENEMY(flamed) || IS_CAN_EXPLODE(flamed))
              Bang(xx, yy);
            else
              RemoveMovingField(xx, yy);
@@ -3259,7 +3205,7 @@ void StartMoving(int x, int y)
     }
     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
     {
-      if (IS_GEM(Feld[newx][newy]))
+      if (IS_FOOD_PIG(Feld[newx][newy]))
       {
        if (IS_MOVING(newx, newy))
          RemoveMovingField(newx, newy);
@@ -4228,22 +4174,22 @@ void MauerWaechst(int x, int y)
     {
       if (MovDir[x][y] == MV_LEFT)
       {
-       if (IN_LEV_FIELD(x - 1, y) && IS_MAUER(Feld[x - 1][y]))
+       if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
          DrawLevelField(x - 1, y);
       }
       else if (MovDir[x][y] == MV_RIGHT)
       {
-       if (IN_LEV_FIELD(x + 1, y) && IS_MAUER(Feld[x + 1][y]))
+       if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
          DrawLevelField(x + 1, y);
       }
       else if (MovDir[x][y] == MV_UP)
       {
-       if (IN_LEV_FIELD(x, y - 1) && IS_MAUER(Feld[x][y - 1]))
+       if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
          DrawLevelField(x, y - 1);
       }
       else
       {
-       if (IN_LEV_FIELD(x, y + 1) && IS_MAUER(Feld[x][y + 1]))
+       if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
          DrawLevelField(x, y + 1);
       }
 
@@ -4342,13 +4288,13 @@ void MauerAbleger(int ax, int ay)
   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
     DrawLevelField(ax, ay);
 
-  if (!IN_LEV_FIELD(ax, ay-1) || IS_MAUER(Feld[ax][ay-1]))
+  if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
     oben_massiv = TRUE;
-  if (!IN_LEV_FIELD(ax, ay+1) || IS_MAUER(Feld[ax][ay+1]))
+  if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
     unten_massiv = TRUE;
-  if (!IN_LEV_FIELD(ax-1, ay) || IS_MAUER(Feld[ax-1][ay]))
+  if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
     links_massiv = TRUE;
-  if (!IN_LEV_FIELD(ax+1, ay) || IS_MAUER(Feld[ax+1][ay]))
+  if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
     rechts_massiv = TRUE;
 
   if (((oben_massiv && unten_massiv) ||
@@ -4590,7 +4536,7 @@ void GameActions()
   byte *recorded_player_action;
   byte summarized_player_action = 0;
 
-  if (game_status != PLAYING)
+  if (game_status != GAME_MODE_PLAYING)
     return;
 
   action_delay_value =
@@ -4616,7 +4562,7 @@ void GameActions()
     HandleNetworking();
 #endif
 
-    if (game_status != PLAYING)
+    if (game_status != GAME_MODE_PLAYING)
       return;
 
     if (!network_player_action_received)
@@ -5206,6 +5152,24 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
   if (!player->active || (!dx && !dy))
     return FALSE;
 
+#if 0
+  {
+    static boolean done = FALSE;
+    static old_count = -1;
+
+    if (FrameCounter < old_count)
+      done = FALSE;
+
+    if (FrameCounter < 100)
+    {
+      printf("::: wanna move [%d] [%d]\n",
+            FrameCounter, player->push_delay_value);
+      done = TRUE;
+      old_count = FrameCounter;
+    }
+  }
+#endif
+
 #if 0
   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
       !tape.playing)
@@ -5695,13 +5659,8 @@ void KillHero(struct PlayerInfo *player)
   if (!player->active)
     return;
 
-#if 1
   /* remove accessible field at the player's position */
   Feld[jx][jy] = EL_EMPTY;
-#else
-  if (IS_PFORTE(Feld[jx][jy]))
-    Feld[jx][jy] = EL_EMPTY;
-#endif
 
   /* deactivate shield (else Bang()/Explode() would not work right) */
   player->shield_normal_time_left = 0;
@@ -5753,17 +5712,31 @@ void RemoveHero(struct PlayerInfo *player)
   ExitY = ZY = jy;
 }
 
-#if 0
 /*
   checkDiagonalPushing()
   -----------------------------------------------------------------------------
   check if diagonal input device direction results in pushing of object
+  (by checking if the alternative direction is walkable, diggable, ...)
 */
 
-static boolean checkDiagonalPushing(int x, int y, int real_dx, int real_dy)
+static boolean checkDiagonalPushing(struct PlayerInfo *player,
+                                   int x, int y, int real_dx, int real_dy)
 {
+  int jx, jy, dx, dy, xx, yy;
+
+  if (real_dx == 0 || real_dy == 0)    /* no diagonal direction => push */
+    return TRUE;
+
+  /* diagonal direction: check alternative direction */
+  jx = player->jx;
+  jy = player->jy;
+  dx = x - jx;
+  dy = y - jy;
+  xx = jx + (dx == 0 ? real_dx : 0);
+  yy = jy + (dy == 0 ? real_dy : 0);
+
+  return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
 }
-#endif
 
 /*
   DigField()
@@ -5783,6 +5756,24 @@ int DigField(struct PlayerInfo *player,
                        dy == +1 ? MV_DOWN : MV_NO_MOVING);
   int element;
 
+#if 0
+  {
+    static boolean done = FALSE;
+
+    if (FrameCounter < 10)
+      done = FALSE;
+
+    if (!done &&
+       real_dx == -1 &&
+       FrameCounter > 10)
+    {
+      printf("::: wanna move left [%d] [%d]\n",
+            FrameCounter, player->push_delay_value);
+      done = TRUE;
+    }
+  }
+#endif
+
   if (player->MovPos == 0)
   {
     player->is_digging = FALSE;
@@ -5802,9 +5793,15 @@ int DigField(struct PlayerInfo *player,
   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
     return MF_NO_ACTION;
 
-  if (IS_TUBE(Feld[jx][jy]))
+#if 0
+  if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
+#else
+  if (IS_TUBE(Feld[jx][jy]) ||
+      (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0)))
+#endif
   {
     int i = 0;
+    int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
     int tube_leave_directions[][2] =
     {
       { EL_TUBE_ANY,                   MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
@@ -5821,7 +5818,7 @@ int DigField(struct PlayerInfo *player,
       { -1,                            MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
     };
 
-    while (tube_leave_directions[i][0] != Feld[jx][jy])
+    while (tube_leave_directions[i][0] != tube_element)
     {
       i++;
       if (tube_leave_directions[i][0] == -1)   /* should not happen */
@@ -6109,14 +6106,17 @@ int DigField(struct PlayerInfo *player,
 
       player->Pushing = TRUE;
 
+#if 0
+      if (element == EL_ROCK)
+       printf("::: wanna push [%d] [%d]\n",
+              FrameCounter, player->push_delay_value);
+#endif
+
       if (!IN_LEV_FIELD(x+dx, y+dy) || !IS_FREE(x+dx, y+dy))
        return MF_NO_ACTION;
 
-      if (real_dy)
-      {
-       if (IN_LEV_FIELD(jx, jy+real_dy) && !IS_SOLID(Feld[jx][jy+real_dy]))
-         return MF_NO_ACTION;
-      }
+      if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
+       return MF_NO_ACTION;
 
       if (player->push_delay == 0)
        player->push_delay = FrameCounter;
@@ -6358,16 +6358,8 @@ int DigField(struct PlayerInfo *player,
                  || !IS_SB_ELEMENT(element))))
        return MF_NO_ACTION;
 
-      if (dx && real_dy)
-      {
-       if (IN_LEV_FIELD(jx, jy+real_dy) && !IS_SOLID(Feld[jx][jy+real_dy]))
-         return MF_NO_ACTION;
-      }
-      else if (dy && real_dx)
-      {
-       if (IN_LEV_FIELD(jx+real_dx, jy) && !IS_SOLID(Feld[jx+real_dx][jy]))
-         return MF_NO_ACTION;
-      }
+      if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
+       return MF_NO_ACTION;
 
       if (player->push_delay == 0)
        player->push_delay = FrameCounter;
@@ -6454,7 +6446,40 @@ int DigField(struct PlayerInfo *player,
       break;
 
     default:
-      if (IS_PUSHABLE(element))
+
+      if (IS_WALKABLE(element))
+      {
+       break;
+      }
+      else if (IS_DIGGABLE(element))
+      {
+       RemoveField(x, y);
+#if 1
+       if (mode != DF_SNAP)
+       {
+         GfxElement[x][y] = element;
+         player->is_digging = TRUE;
+       }
+#endif
+       PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
+
+       break;
+      }
+      else if (IS_COLLECTIBLE(element))
+      {
+       RemoveField(x, y);
+#if 1
+       if (mode != DF_SNAP)
+       {
+         GfxElement[x][y] = element;
+         player->is_collecting = TRUE;
+       }
+#endif
+       PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+
+       break;
+      }
+      else if (IS_PUSHABLE(element))
       {
        if (mode == DF_SNAP)
          return MF_NO_ACTION;
@@ -6467,16 +6492,8 @@ int DigField(struct PlayerInfo *player,
        if (!IN_LEV_FIELD(x+dx, y+dy) || !IS_FREE(x+dx, y+dy))
          return MF_NO_ACTION;
 
-       if (dx && real_dy)
-       {
-         if (IN_LEV_FIELD(jx, jy+real_dy) && !IS_SOLID(Feld[jx][jy+real_dy]))
-           return MF_NO_ACTION;
-       }
-       else if (dy && real_dx)
-       {
-         if (IN_LEV_FIELD(jx+real_dx, jy) && !IS_SOLID(Feld[jx+real_dx][jy]))
-           return MF_NO_ACTION;
-       }
+       if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
+         return MF_NO_ACTION;
 
        if (player->push_delay == 0)
          player->push_delay = FrameCounter;
@@ -6569,6 +6586,11 @@ boolean PlaceBomb(struct PlayerInfo *player)
       IS_ACTIVE_BOMB(element) || element == EL_EXPLOSION)
     return FALSE;
 
+#if 0
+  if (element != EL_EMPTY)
+    return FALSE;
+#endif
+
   if (element != EL_EMPTY)
   {
 #if 0
@@ -6793,7 +6815,7 @@ void RequestQuitGame(boolean ask_if_really_quit)
     else
 #endif
     {
-      game_status = MAINMENU;
+      game_status = GAME_MODE_MAIN;
       DrawMainMenu();
     }
   }
@@ -6954,7 +6976,7 @@ static void HandleGameButtons(struct GadgetInfo *gi)
 {
   int id = gi->custom_id;
 
-  if (game_status != PLAYING)
+  if (game_status != GAME_MODE_PLAYING)
     return;
 
   switch (id)