rocksndiamonds-3.1.0
[rocksndiamonds.git] / src / game.c
index 467603a896db9d65bcd5dd2e4875e11c0f8ccf79..762035dc5656bc5479fc3158d263620b0675cb1c 100644 (file)
        ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element :     \
         (e) == EL_TRIGGER_PLAYER  ? (ch)->actual_trigger_player : (e))
 
+#define GET_VALID_PLAYER_ELEMENT(e)                                    \
+       ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
+
 #define CAN_GROW_INTO(e)                                               \
-       (e == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
+       ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
 
 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                        \
                (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
@@ -609,31 +612,39 @@ struct
 }
 access_direction_list[] =
 {
-  { EL_TUBE_ANY,               MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
-  { EL_TUBE_VERTICAL,                               MV_UP | MV_DOWN },
-  { EL_TUBE_HORIZONTAL,                MV_LEFT | MV_RIGHT                   },
-  { EL_TUBE_VERTICAL_LEFT,     MV_LEFT |            MV_UP | MV_DOWN },
-  { EL_TUBE_VERTICAL_RIGHT,              MV_RIGHT | MV_UP | MV_DOWN },
-  { EL_TUBE_HORIZONTAL_UP,     MV_LEFT | MV_RIGHT | MV_UP           },
-  { EL_TUBE_HORIZONTAL_DOWN,   MV_LEFT | MV_RIGHT |         MV_DOWN },
-  { EL_TUBE_LEFT_UP,           MV_LEFT |            MV_UP           },
-  { EL_TUBE_LEFT_DOWN,         MV_LEFT |                    MV_DOWN },
-  { EL_TUBE_RIGHT_UP,                    MV_RIGHT | MV_UP           },
-  { EL_TUBE_RIGHT_DOWN,                          MV_RIGHT |         MV_DOWN },
-
-  { EL_SP_PORT_LEFT,                     MV_RIGHT                   },
-  { EL_SP_PORT_RIGHT,          MV_LEFT                              },
-  { EL_SP_PORT_UP,                                          MV_DOWN },
-  { EL_SP_PORT_DOWN,                                MV_UP           },
-  { EL_SP_PORT_HORIZONTAL,     MV_LEFT | MV_RIGHT                   },
-  { EL_SP_PORT_VERTICAL,                            MV_UP | MV_DOWN },
-  { EL_SP_PORT_ANY,            MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
-  { EL_SP_GRAVITY_PORT_LEFT,             MV_RIGHT                   },
-  { EL_SP_GRAVITY_PORT_RIGHT,  MV_LEFT                              },
-  { EL_SP_GRAVITY_PORT_UP,                                  MV_DOWN },
-  { EL_SP_GRAVITY_PORT_DOWN,                        MV_UP           },
-
-  { EL_UNDEFINED,              MV_NO_MOVING                         }
+  { EL_TUBE_ANY,                       MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
+  { EL_TUBE_VERTICAL,                                       MV_UP | MV_DOWN },
+  { EL_TUBE_HORIZONTAL,                        MV_LEFT | MV_RIGHT                   },
+  { EL_TUBE_VERTICAL_LEFT,             MV_LEFT |            MV_UP | MV_DOWN },
+  { EL_TUBE_VERTICAL_RIGHT,                      MV_RIGHT | MV_UP | MV_DOWN },
+  { EL_TUBE_HORIZONTAL_UP,             MV_LEFT | MV_RIGHT | MV_UP           },
+  { EL_TUBE_HORIZONTAL_DOWN,           MV_LEFT | MV_RIGHT |         MV_DOWN },
+  { EL_TUBE_LEFT_UP,                   MV_LEFT |            MV_UP           },
+  { EL_TUBE_LEFT_DOWN,                 MV_LEFT |                    MV_DOWN },
+  { EL_TUBE_RIGHT_UP,                            MV_RIGHT | MV_UP           },
+  { EL_TUBE_RIGHT_DOWN,                                  MV_RIGHT |         MV_DOWN },
+
+  { EL_SP_PORT_LEFT,                             MV_RIGHT                   },
+  { EL_SP_PORT_RIGHT,                  MV_LEFT                              },
+  { EL_SP_PORT_UP,                                                  MV_DOWN },
+  { EL_SP_PORT_DOWN,                                        MV_UP           },
+  { EL_SP_PORT_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
+  { EL_SP_PORT_VERTICAL,                                    MV_UP | MV_DOWN },
+  { EL_SP_PORT_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
+  { EL_SP_GRAVITY_PORT_LEFT,                     MV_RIGHT                   },
+  { EL_SP_GRAVITY_PORT_RIGHT,          MV_LEFT                              },
+  { EL_SP_GRAVITY_PORT_UP,                                          MV_DOWN },
+  { EL_SP_GRAVITY_PORT_DOWN,                                MV_UP           },
+  { EL_SP_GRAVITY_ON_PORT_LEFT,                          MV_RIGHT                   },
+  { EL_SP_GRAVITY_ON_PORT_RIGHT,       MV_LEFT                              },
+  { EL_SP_GRAVITY_ON_PORT_UP,                                       MV_DOWN },
+  { EL_SP_GRAVITY_ON_PORT_DOWN,                                     MV_UP           },
+  { EL_SP_GRAVITY_OFF_PORT_LEFT,                 MV_RIGHT                   },
+  { EL_SP_GRAVITY_OFF_PORT_RIGHT,      MV_LEFT                              },
+  { EL_SP_GRAVITY_OFF_PORT_UP,                                      MV_DOWN },
+  { EL_SP_GRAVITY_OFF_PORT_DOWN,                            MV_UP           },
+
+  { EL_UNDEFINED,                      MV_NO_MOVING                         }
 };
 
 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
@@ -2109,7 +2120,7 @@ void InitMovDir(int x, int y)
        {
 #if 1
          /* use random direction as default start direction */
-         if (game.engine_version >= VERSION_IDENT(3,1,0,2))
+         if (game.engine_version >= VERSION_IDENT(3,1,0,0))
            MovDir[x][y] = 1 << RND(4);
 #endif
 
@@ -2818,7 +2829,11 @@ void DrawRelocatePlayer(struct PlayerInfo *player)
 
 void RelocatePlayer(int jx, int jy, int el_player_raw)
 {
+#if 1
+  int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
+#else
   int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
+#endif
   struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
   boolean ffwd_delay = (tape.playing && tape.fast_forward);
   boolean no_delay = (tape.warp_forward);
@@ -2916,10 +2931,20 @@ void RelocatePlayer(int jx, int jy, int el_player_raw)
   TestIfPlayerTouchesCustomElement(jx, jy);
 #endif
 
-#if 1
+#if 0
+  printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
+#endif
+
+#if 0
+#if 0
+  /* needed to allow change of walkable custom element by entering player */
+  if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
+    Changed[jx][jy] = 0;       /* allow another change (but prevent loop) */
+#else
   /* needed to allow change of walkable custom element by entering player */
   Changed[jx][jy] = 0;         /* allow another change */
 #endif
+#endif
 
 #if 0
   printf("::: player entering %d, %d from %s ...\n", jx, jy,
@@ -3052,10 +3077,17 @@ void Explode(int ex, int ey, int phase, int mode)
        continue;
 #else
       /* indestructible elements can only explode in center (but not flames) */
+#if 1
+      if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
+                                          mode == EX_TYPE_BORDER)) ||
+         element == EL_FLAMES)
+       continue;
+#else
       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
          element == EL_FLAMES)
        continue;
 #endif
+#endif
 
 #else
       if ((IS_INDESTRUCTIBLE(element) &&
@@ -3068,7 +3100,7 @@ void Explode(int ex, int ey, int phase, int mode)
 #if 1
       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
          (game.engine_version < VERSION_IDENT(3,1,0,0) ||
-          (x == ex && y == ey)))
+          (x == ex && y == ey && mode != EX_TYPE_BORDER)))
 #else
       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
 #endif
@@ -3083,6 +3115,12 @@ void Explode(int ex, int ey, int phase, int mode)
          Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
          Store[x][y] = 0;
 #endif
+
+#if 0
+       printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
+              element_info[Feld[x][y]].token_name,
+              Store[x][y], Store2[x][y]);
+#endif
        }
 
        continue;
@@ -3094,9 +3132,15 @@ void Explode(int ex, int ey, int phase, int mode)
        Back[x][y] = element;
 #else
 #if 1
+#if 1
+      if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
+         (x != ex || y != ey || mode == EX_TYPE_BORDER))
+       Back[x][y] = element;
+#else
       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
          (x != ex || y != ey))
        Back[x][y] = element;
+#endif
 #else
       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
        Back[x][y] = element;
@@ -3185,10 +3229,15 @@ void Explode(int ex, int ey, int phase, int mode)
       else
        Store[x][y] = EL_EMPTY;
 
-      if (x != ex || y != ey ||
-         center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_TYPE_BORDER)
+      if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
+         center_element == EL_AMOEBA_TO_DIAMOND)
        Store2[x][y] = element;
 
+#if 0
+      printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
+            element_info[Store2[x][y]].token_name);
+#endif
+
 #if 0
       if (AmoebaNr[x][y] &&
          (element == EL_AMOEBA_FULL ||
@@ -3235,6 +3284,11 @@ void Explode(int ex, int ey, int phase, int mode)
       game.yamyam_content_nr =
        (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
 
+#if 0
+  printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
+        element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
+#endif
+
     return;
   }
 
@@ -3288,6 +3342,11 @@ void Explode(int ex, int ey, int phase, int mode)
     border_element = StorePlayer[x][y];
 #endif
 
+#if 0
+  printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
+        element_info[border_element].token_name, Store2[x][y]);
+#endif
+
 #if 0
   printf("::: phase == %d\n", phase);
 #endif
@@ -3318,6 +3377,11 @@ void Explode(int ex, int ey, int phase, int mode)
     }
     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
     {
+#if 0
+      printf("::: %d,%d: %d %s\n", x, y, border_element,
+            element_info[border_element].token_name);
+#endif
+
       Feld[x][y] = Store2[x][y];
       Store2[x][y] = 0;
       Bang(x, y);
@@ -5380,7 +5444,7 @@ void StartMoving(int x, int y)
 
 #if 1
     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
-       CheckCollision[x][y] && IN_LEV_FIELD_AND_NOT_FREE(newx, newy))
+       CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
 #else
     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
        WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
@@ -7377,6 +7441,11 @@ static void ChangeElementNowExt(int x, int y, int target_element)
   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
       IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
   {
+#if 0
+    printf("::: BOOOM! [%d, '%s']\n", target_element,
+          element_info[target_element].token_name);
+#endif
+
     Bang(x, y);
     return;
   }
@@ -7417,7 +7486,9 @@ static void ChangeElementNowExt(int x, int y, int target_element)
       DrawLevelFieldCrumbledSandNeighbours(x, y);
   }
 
+#if 0
   Changed[x][y] |= ChangeEvent[x][y];  /* ignore same changes in this frame */
+#endif
 
 #if 0
   TestIfBadThingTouchesHero(x, y);
@@ -7425,9 +7496,14 @@ static void ChangeElementNowExt(int x, int y, int target_element)
   TestIfElementTouchesCustomElement(x, y);
 #endif
 
+  /* "Changed[][]" not set yet to allow "entered by player" change one time */
   if (ELEM_IS_PLAYER(target_element))
     RelocatePlayer(x, y, target_element);
 
+#if 1
+  Changed[x][y] |= ChangeEvent[x][y];  /* ignore same changes in this frame */
+#endif
+
 #if 1
   TestIfBadThingTouchesHero(x, y);
   TestIfPlayerTouchesCustomElement(x, y);
@@ -7514,6 +7590,16 @@ static boolean ChangeElementNow(int x, int y, int element, int page)
        continue;
       }
 
+#if 0
+      if (Changed[ex][ey])     /* do not change already changed elements */
+      {
+       can_replace[xx][yy] = FALSE;
+       complete_replace = FALSE;
+
+       continue;
+      }
+#endif
+
       e = Feld[ex][ey];
 
       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
@@ -7537,6 +7623,7 @@ static boolean ChangeElementNow(int x, int y, int element, int page)
 #endif
 
 #endif
+
       is_walkable     = (is_empty || IS_WALKABLE(e));
       is_diggable     = (is_empty || IS_DIGGABLE(e));
       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
@@ -7544,12 +7631,13 @@ static boolean ChangeElementNow(int x, int y, int element, int page)
       is_removable    = (is_diggable || is_collectible);
 
       can_replace[xx][yy] =
-       ((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
-        (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
-        (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
-        (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
-        (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
-        (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible));
+       (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
+         (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
+         (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
+         (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
+         (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
+         (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
+        !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
 
       if (!can_replace[xx][yy])
        complete_replace = FALSE;
@@ -9077,7 +9165,9 @@ static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
 #if 1
   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
          IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
+#if 0
          (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
+#endif
          (IS_DIGGABLE(Feld[newx][newy]) ||
           IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
           canPassField(newx, newy, move_dir)));
@@ -9769,7 +9859,12 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
       TestIfHeroTouchesBadThing(jx, jy);
       TestIfPlayerTouchesCustomElement(jx, jy);
 #if 1
-      TestIfElementTouchesCustomElement(jx, jy);       /* for empty space */
+#if 1
+      /* needed because pushed element has not yet reached its destination,
+        so it would trigger a change event at its previous field location */
+      if (!player->is_pushing)
+#endif
+       TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
 #endif
 
       if (!player->active)
@@ -9956,7 +10051,7 @@ void TestIfElementTouchesCustomElement(int x, int y)
   boolean change_center_element = FALSE;
   int center_element_change_page = 0;
   int center_element = Feld[x][y];     /* should always be non-moving! */
-  int border_trigger_element;
+  int border_trigger_element = EL_UNDEFINED;
   int i, j;
 
   for (i = 0; i < NUM_DIRECTIONS; i++)
@@ -10965,6 +11060,16 @@ int DigField(struct PlayerInfo *player,
              element == EL_SP_GRAVITY_PORT_UP ||
              element == EL_SP_GRAVITY_PORT_DOWN)
            game.gravity = !game.gravity;
+         else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
+                  element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
+                  element == EL_SP_GRAVITY_ON_PORT_UP ||
+                  element == EL_SP_GRAVITY_ON_PORT_DOWN)
+           game.gravity = TRUE;
+         else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
+                  element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
+                  element == EL_SP_GRAVITY_OFF_PORT_UP ||
+                  element == EL_SP_GRAVITY_OFF_PORT_DOWN)
+           game.gravity = FALSE;
        }
 
        /* automatically move to the next field with double speed */
@@ -11709,11 +11814,11 @@ boolean DropElement(struct PlayerInfo *player)
     nexty = dropy + GET_DY_FROM_DIR(move_direction);
 
 #if 1
-      Changed[dropx][dropy] = 0;       /* allow another change */
-      CheckCollision[dropx][dropy] = 2;
+    Changed[dropx][dropy] = 0;         /* allow another change */
+    CheckCollision[dropx][dropy] = 2;
 #else
 
-    if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
+    if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
     {
 #if 0
       WasJustMoving[dropx][dropy] = 3;
@@ -11724,7 +11829,8 @@ boolean DropElement(struct PlayerInfo *player)
 #endif
 #endif
     }
-#if 1
+#if 0
+    /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
     else
     {
       Changed[dropx][dropy] = 0;       /* allow another change */