rnd-20040117-2-src
[rocksndiamonds.git] / src / game.c
index 1f3b8e02fb64939b490c64c05318e8f3220ce748..dfad329ef9c2b2e64d5df8fb4e1a4c6986edd595 100644 (file)
 #define GET_MAX_MOVE_DELAY(e)  (   (element_info[e].move_delay_fixed) + \
                                    (element_info[e].move_delay_random))
 
+#if 1
+#define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition)            \
+               (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
+                                       (condition) ||                  \
+                                       (DONT_COLLIDE_WITH(e) &&        \
+                                        IS_PLAYER(x, y) &&             \
+                                        !PLAYER_PROTECTED(x, y))))
+#else
 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition)            \
                (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
                                        (condition) ||                  \
                                        (DONT_COLLIDE_WITH(e) &&        \
                                         IS_FREE_OR_PLAYER(x, y))))
+#endif
 
 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition)             \
                (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
                                        Feld[x][y] == EL_EXIT_OPEN ||   \
                                        Feld[x][y] == EL_ACID))
 
+#if 0
 #if 1
 #define MAZE_RUNNER_CAN_ENTER_FIELD(x, y)                              \
                (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
                (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
                                        IS_FOOD_DARK_YAMYAM(Feld[x][y])))
 #endif
+#endif
+
+#define MOVE_ENTER_EL(e)       (element_info[e].move_enter_element)
+#define IS_IN_GROUP(e, g)      (element_info[e].in_group[g] == TRUE)
+#define IS_IN_GROUP_EL(e, ge)  (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
+
+#define CE_ENTER_FIELD_COND(e, x, y)                                   \
+               (!IS_PLAYER(x, y) &&                                    \
+                (Feld[x][y] == EL_ACID ||                              \
+                 Feld[x][y] == MOVE_ENTER_EL(e) ||                     \
+                 (IS_GROUP_ELEMENT(MOVE_ENTER_EL(e)) &&                \
+                  IS_IN_GROUP_EL(Feld[x][y], MOVE_ENTER_EL(e)))))
+
+#define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
+       ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
 
 #define MOLE_CAN_ENTER_FIELD(x, y, condition)                          \
                (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
@@ -609,6 +634,18 @@ static void InitField(int x, int y, boolean init_game)
       InitPlayerField(x, y, element, init_game);
       break;
 
+    case EL_SOKOBAN_FIELD_PLAYER:
+      element = Feld[x][y] = EL_PLAYER_1;
+      InitField(x, y, init_game);
+
+      element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
+      InitField(x, y, init_game);
+      break;
+
+    case EL_SOKOBAN_FIELD_EMPTY:
+      local_player->sokobanfields_still_needed++;
+      break;
+
     case EL_STONEBLOCK:
       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
        Feld[x][y] = EL_ACID_POOL_TOPLEFT;
@@ -686,10 +723,6 @@ static void InitField(int x, int y, boolean init_game)
       local_player->lights_still_needed++;
       break;
 
-    case EL_SOKOBAN_FIELD_EMPTY:
-      local_player->sokobanfields_still_needed++;
-      break;
-
     case EL_PENGUIN:
       local_player->friends_still_needed++;
       break;
@@ -1690,23 +1723,36 @@ void InitMovDir(int x, int y)
     default:
       if (IS_CUSTOM_ELEMENT(element))
       {
-       if (element_info[element].move_direction_initial != MV_NO_MOVING)
-         MovDir[x][y] = element_info[element].move_direction_initial;
-       else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
-                element_info[element].move_pattern == MV_TURNING_LEFT ||
-                element_info[element].move_pattern == MV_TURNING_RIGHT ||
-                element_info[element].move_pattern == MV_TURNING_LEFT_RIGHT ||
-                element_info[element].move_pattern == MV_TURNING_RIGHT_LEFT ||
-                element_info[element].move_pattern == MV_TURNING_RANDOM)
+       struct ElementInfo *ei = &element_info[element];
+       int move_direction_initial = ei->move_direction_initial;
+       int move_pattern = ei->move_pattern;
+
+       if (move_direction_initial == MV_PREVIOUS)
+       {
+         if (MovDir[x][y] != MV_NO_MOVING)
+           return;
+
+         move_direction_initial = MV_AUTOMATIC;
+       }
+
+       if (move_direction_initial & MV_ANY_DIRECTION)
+         MovDir[x][y] = move_direction_initial;
+       else if (move_direction_initial == MV_RANDOM ||
+                move_pattern == MV_ALL_DIRECTIONS ||
+                move_pattern == MV_TURNING_LEFT ||
+                move_pattern == MV_TURNING_RIGHT ||
+                move_pattern == MV_TURNING_LEFT_RIGHT ||
+                move_pattern == MV_TURNING_RIGHT_LEFT ||
+                move_pattern == MV_TURNING_RANDOM)
          MovDir[x][y] = 1 << RND(4);
-       else if (element_info[element].move_pattern == MV_HORIZONTAL)
+       else if (move_pattern == MV_HORIZONTAL)
          MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
-       else if (element_info[element].move_pattern == MV_VERTICAL)
+       else if (move_pattern == MV_VERTICAL)
          MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
-       else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
+       else if (move_pattern & MV_ANY_DIRECTION)
          MovDir[x][y] = element_info[element].move_pattern;
-       else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
-                element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
+       else if (move_pattern == MV_ALONG_LEFT_SIDE ||
+                move_pattern == MV_ALONG_RIGHT_SIDE)
        {
          for (i = 0; i < 4; i++)
          {
@@ -1715,7 +1761,7 @@ void InitMovDir(int x, int y)
 
            if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
            {
-             if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
+             if (move_pattern == MV_ALONG_RIGHT_SIDE)
                MovDir[x][y] = direction[0][i];
              else
                MovDir[x][y] = direction[1][i];
@@ -3793,8 +3839,10 @@ inline static void TurnRoundExt(int x, int y)
           move_pattern == MV_TURNING_RANDOM ||
           move_pattern == MV_ALL_DIRECTIONS)
   {
-    boolean can_turn_left  = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
-    boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
+    boolean can_turn_left =
+      CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
+    boolean can_turn_right =
+      CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
 
     if (move_pattern == MV_TURNING_LEFT)
       MovDir[x][y] = left_dir;
@@ -3838,9 +3886,9 @@ inline static void TurnRoundExt(int x, int y)
   }
   else if (move_pattern == MV_ALONG_LEFT_SIDE)
   {
-    if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
+    if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
       MovDir[x][y] = left_dir;
-    else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
+    else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
       MovDir[x][y] = right_dir;
 
     if (MovDir[x][y] != old_move_dir)
@@ -3848,9 +3896,9 @@ inline static void TurnRoundExt(int x, int y)
   }
   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
   {
-    if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
+    if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
       MovDir[x][y] = right_dir;
-    else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
+    else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
       MovDir[x][y] = left_dir;
 
     if (MovDir[x][y] != old_move_dir)
@@ -3910,14 +3958,14 @@ inline static void TurnRoundExt(int x, int y)
        new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
       Moving2Blocked(x, y, &newx, &newy);
 
-      if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
+      if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
        return;
 
       MovDir[x][y] =
        new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
       Moving2Blocked(x, y, &newx, &newy);
 
-      if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
+      if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
        return;
 
       MovDir[x][y] = old_move_dir;
@@ -3931,8 +3979,7 @@ inline static void TurnRoundExt(int x, int y)
 
     MovDelay[x][y] = 0;
   }
-  else if (move_pattern & MV_MAZE_RUNNER_STYLE ||
-          element == EL_MAZE_RUNNER)
+  else if (move_pattern & MV_MAZE_RUNNER_STYLE)
   {
     static int test_xy[7][2] =
     {
@@ -3976,7 +4023,7 @@ inline static void TurnRoundExt(int x, int y)
        break;
       }
 
-      if (!MAZE_RUNNER_CAN_ENTER_FIELD(xx, yy))
+      if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
        continue;
 
       move_dir_preference = -1 * RunnerVisit[xx][yy];
@@ -4580,6 +4627,7 @@ void StartMoving(int x, int y)
     {
 #if 1
       TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
+
       return;
 #else
       /* player killed by element which is deadly when colliding with */
@@ -4638,6 +4686,7 @@ void StartMoving(int x, int y)
          DrawPlayerField(x, y);
        else
          DrawLevelField(x, y);
+
        return;
       }
     }
@@ -4661,42 +4710,78 @@ void StartMoving(int x, int y)
          DrawPlayerField(x, y);
        else
          DrawLevelField(x, y);
+
        return;
       }
     }
-    else if ((move_pattern & MV_MAZE_RUNNER_STYLE ||
-             element == EL_MAZE_RUNNER) && IN_LEV_FIELD(newx, newy))
-    {
+
 #if 1
-      if (IS_FREE(newx, newy))
-#else
-      if (IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
+
+    /*
+    else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
+    */
+
+    else if (IS_CUSTOM_ELEMENT(element) &&
+            CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
+
+#if 0
+ &&
+            !IS_FREE(newx, newy)
 #endif
+
+)
+    {
+#if 0
+      printf("::: '%s' digs '%s' [%d]\n",
+            element_info[element].token_name,
+            element_info[Feld[newx][newy]].token_name,
+            StorePlayer[newx][newy]);
+#endif
+
+      if (!IS_FREE(newx, newy))
       {
+       int new_element = Feld[newx][newy];
+       int sound;
+
+       /* no element can dig solid indestructible elements */
+       if (IS_INDESTRUCTIBLE(new_element) &&
+           !IS_DIGGABLE(new_element) &&
+           !IS_COLLECTIBLE(new_element))
+         return;
+
+       if (AmoebaNr[newx][newy] &&
+           (new_element == EL_AMOEBA_FULL ||
+            new_element == EL_BD_AMOEBA ||
+            new_element == EL_AMOEBA_GROWING))
+       {
+         AmoebaCnt[AmoebaNr[newx][newy]]--;
+         AmoebaCnt2[AmoebaNr[newx][newy]]--;
+       }
+
        if (IS_MOVING(newx, newy))
          RemoveMovingField(newx, newy);
        else
        {
-         Feld[newx][newy] = EL_EMPTY;
+         RemoveField(newx, newy);
          DrawLevelField(newx, newy);
        }
 
-       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
+       sound = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
+                IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
+                ACTION_BREAKING);
+
+       PlayLevelSoundAction(x, y, sound);
       }
-      else if (!IS_FREE(newx, newy))
+
+      if (move_pattern & MV_MAZE_RUNNER_STYLE)
       {
-#if 0
-       if (IS_PLAYER(x, y))
-         DrawPlayerField(x, y);
-       else
-         DrawLevelField(x, y);
-#endif
-       return;
+       RunnerVisit[x][y] = FrameCounter;
+       PlayerVisit[x][y] /= 8;         /* expire player visit path */
       }
-
-      RunnerVisit[x][y] = FrameCounter;
-      PlayerVisit[x][y] /= 8;          /* expire player visit path */
     }
+
+#endif
+
     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
     {
       if (!IS_FREE(newx, newy))
@@ -4804,6 +4889,7 @@ void StartMoving(int x, int y)
        DrawLevelField(x, y);
 
        MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
+
        return;                         /* wait for shrinking amoeba */
       }
       else     /* element == EL_PACMAN */
@@ -4983,6 +5069,25 @@ void ContinueMoving(int x, int y)
 
   ResetGfxAnimation(x, y);     /* reset animation values for old field */
 
+#if 1
+  if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y))
+  {
+    int new_element = element_info[element].move_leave_element;
+
+    Feld[x][y] = new_element;
+
+    if (new_element != EL_EMPTY)
+    {
+      InitField(x, y, FALSE);
+
+      TestIfElementTouchesCustomElement(x, y);
+
+      if (GFX_CRUMBLED(new_element))
+       DrawLevelFieldCrumbledSandNeighbours(x, y);
+    }
+  }
+#endif
+
 #if 0
   /* 2.1.1 (does not work correctly for spring) */
   if (!CAN_MOVE(element))
@@ -6034,6 +6139,8 @@ static void ChangeActiveTrap(int x, int y)
 
 static void ChangeElementNowExt(int x, int y, int target_element)
 {
+  int previous_move_direction = MovDir[x][y];
+
   /* check if element under player changes from accessible to unaccessible
      (needed for special case of dropping element which then changes) */
   if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
@@ -6051,6 +6158,9 @@ static void ChangeElementNowExt(int x, int y, int target_element)
   ResetGfxAnimation(x, y);
   ResetRandomAnimationValue(x, y);
 
+  if (element_info[Feld[x][y]].move_direction_initial == MV_PREVIOUS)
+    MovDir[x][y] = previous_move_direction;
+
   InitField(x, y, FALSE);
   if (CAN_MOVE(Feld[x][y]))
     InitMovDir(x, y);
@@ -9294,7 +9404,7 @@ boolean DropElement(struct PlayerInfo *player)
     int move_stepsize = element_info[new_element].move_stepsize;
     int direction, dx, dy, nextx, nexty;
 
-    if (element_info[new_element].move_direction_initial == MV_NO_MOVING)
+    if (element_info[new_element].move_direction_initial == MV_AUTOMATIC)
       MovDir[jx][jy] = player->MovDir;
 
     direction = MovDir[jx][jy];