rnd-20040117-2-src
[rocksndiamonds.git] / src / game.c
index f29584b4be06e36a1a0807689abb5dc35555826b..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;
@@ -763,6 +796,15 @@ static void InitField(int x, int y, boolean init_game)
     default:
       if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
        InitMovDir(x, y);
+      else if (IS_GROUP_ELEMENT(element))
+      {
+       struct ElementGroupInfo *group = element_info[element].group;
+       int random_pos = RND(group->num_elements_resolved);
+
+       Feld[x][y] = group->element_resolved[random_pos];
+
+       InitField(x, y, init_game);
+      }
       break;
   }
 }
@@ -787,10 +829,9 @@ void DrawGameDoorValues()
           int2str(TimeLeft, 3), FONT_TEXT_2);
 }
 
-#if 1
-
 static void resolve_group_element(int group_element, int recursion_depth)
 {
+  static int group_nr;
   static struct ElementGroupInfo *group;
   struct ElementGroupInfo *actual_group = element_info[group_element].group;
   int i;
@@ -810,6 +851,7 @@ static void resolve_group_element(int group_element, int recursion_depth)
   {
     group = element_info[group_element].group;
     group->num_elements_resolved = 0;
+    group_nr = group_element - EL_GROUP_START;
   }
 
   for (i = 0; i < actual_group->num_elements; i++)
@@ -822,10 +864,13 @@ static void resolve_group_element(int group_element, int recursion_depth)
     if (IS_GROUP_ELEMENT(element))
       resolve_group_element(element, recursion_depth + 1);
     else
+    {
       group->element_resolved[group->num_elements_resolved++] = element;
+      element_info[element].in_group[group_nr] = TRUE;
+    }
   }
 
-#if 1
+#if 0
   if (recursion_depth == 0 && group_element <= EL_GROUP_4)
   {
     printf("::: group %d: %d resolved elements\n",
@@ -837,58 +882,6 @@ static void resolve_group_element(int group_element, int recursion_depth)
 #endif
 }
 
-#else
-
-static void resolve_group_element(int group_element, int recursion_depth)
-{
-  static short element_list_count[NUM_FILE_ELEMENTS];
-  struct ElementGroupInfo *group = element_info[group_element].group;
-  int i, j;
-
-  if (group == NULL)
-    return;
-
-  if (recursion_depth > NUM_GROUP_ELEMENTS)    /* recursion too deep */
-    return;
-
-  if (recursion_depth == 0)                    /* initialization */
-    for (i = 0; i < NUM_FILE_ELEMENTS; i++)
-      element_list_count[i] = 0;
-
-  for (i = 0; i < group->num_elements; i++)
-  {
-    int element = group->element[i];
-
-    if (IS_GROUP_ELEMENT(element))
-      resolve_group_element(element, recursion_depth + 1);
-    else if (element < NUM_FILE_ELEMENTS)
-      element_list_count[group->element[i]]++;
-  }
-
-  if (recursion_depth == 0)                    /* finalization */
-  {
-    group->num_elements_resolved = 0;
-
-    for (i = 0; i < NUM_FILE_ELEMENTS; i++)
-      for (j = 0; j < element_list_count[i]; j++)
-       if (group->num_elements_resolved < NUM_FILE_ELEMENTS)
-         group->element_resolved[group->num_elements_resolved++] = i;
-
-#if 1
-    if (group_element <= EL_GROUP_8)
-    {
-      printf("::: group %d: %d resolved elements\n",
-            group_element - EL_GROUP_START, group->num_elements_resolved);
-      for (i = 0; i < group->num_elements_resolved; i++)
-       printf("::: - %d ['%s']\n", group->element_resolved[i],
-              element_info[group->element_resolved[i]].token_name);
-    }
-#endif
-  }
-}
-
-#endif
-
 
 /*
   =============================================================================
@@ -919,6 +912,10 @@ static void InitGameEngine()
 
   /* ---------- recursively resolve group elements ------------------------- */
 
+  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+    for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
+      element_info[i].in_group[j] = FALSE;
+
   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
     resolve_group_element(EL_GROUP_START + i, 0);
 
@@ -1726,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++)
          {
@@ -1751,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];
@@ -3829,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;
@@ -3874,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)
@@ -3884,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)
@@ -3946,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;
@@ -3967,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] =
     {
@@ -4012,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];
@@ -4616,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 */
@@ -4674,6 +4686,7 @@ void StartMoving(int x, int y)
          DrawPlayerField(x, y);
        else
          DrawLevelField(x, y);
+
        return;
       }
     }
@@ -4697,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))
@@ -4840,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 */
@@ -5019,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))
@@ -6070,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) &&
@@ -6087,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);
@@ -9330,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];