rnd-20031011-2-src
[rocksndiamonds.git] / src / game.c
index 007b3d14fc18c058b2f7af6f4c482cf5409b4e85..caad2f990c5ef80cdb5fc91e020bef69cdb32c3e 100644 (file)
@@ -169,6 +169,7 @@ static void KillHeroUnlessProtected(int, int);
 static void TestIfPlayerTouchesCustomElement(int, int);
 static void TestIfElementTouchesCustomElement(int, int);
 
+static void ChangeElement(int, int, int);
 static boolean CheckTriggeredElementSideChange(int, int, int, int, int);
 static boolean CheckTriggeredElementChange(int, int, int, int);
 static boolean CheckElementSideChange(int, int, int, int, int, int);
@@ -251,6 +252,22 @@ static struct ChangingElementInfo change_delay_list[] =
     NULL,
     NULL
   },
+  {
+    EL_SP_EXIT_OPENING,
+    EL_SP_EXIT_OPEN,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_SP_EXIT_CLOSING,
+    EL_SP_EXIT_CLOSED,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
   {
     EL_SWITCHGATE_OPENING,
     EL_SWITCHGATE_OPEN,
@@ -879,7 +896,7 @@ static void InitGameEngine()
 
     for (j=0; j < ei->num_change_pages; j++)
     {
-      if (!ei->change_page->can_change)
+      if (!ei->change_page[j].can_change)
        continue;
 
       if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
@@ -1086,6 +1103,7 @@ void InitGame()
   game.timegate_time_left = 0;
   game.switchgate_pos = 0;
   game.balloon_dir = MV_NO_MOVING;
+  game.gravity = level.initial_gravity;
   game.explosions_delayed = TRUE;
 
   game.envelope_active = FALSE;
@@ -1106,6 +1124,7 @@ void InitGame()
       Feld[x][y] = level.field[x][y];
       MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
       ChangeDelay[x][y] = 0;
+      ChangePage[x][y] = -1;
       Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
       AmoebaNr[x][y] = 0;
       JustStopped[x][y] = 0;
@@ -1671,11 +1690,15 @@ void GameWon()
   }
 
   /* close exit door after last player */
-  if (Feld[ExitX][ExitY] == EL_EXIT_OPEN && AllPlayersGone)
+  if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
+       Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
   {
-    Feld[ExitX][ExitY] = EL_EXIT_CLOSING;
+    int element = Feld[ExitX][ExitY];
+
+    Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
+                         EL_SP_EXIT_CLOSING);
 
-    PlaySoundLevelElementAction(ExitX, ExitY, EL_EXIT_OPEN, ACTION_CLOSING);
+    PlaySoundLevelElementAction(ExitX, ExitY, element, ACTION_CLOSING);
   }
 
   /* Hero disappears */
@@ -1917,6 +1940,7 @@ static void RemoveField(int x, int y)
 
   AmoebaNr[x][y] = 0;
   ChangeDelay[x][y] = 0;
+  ChangePage[x][y] = -1;
   Pushed[x][y] = FALSE;
 
   GfxElement[x][y] = EL_UNDEFINED;
@@ -2030,6 +2054,11 @@ void RelocatePlayer(int x, int y, int element)
 {
   struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
 
+#if 1
+  RemoveField(x, y);
+  DrawLevelField(x, y);
+#endif
+
   if (player->present)
   {
     while (player->MovPos)
@@ -2037,14 +2066,23 @@ void RelocatePlayer(int x, int y, int element)
       ScrollFigure(player, SCROLL_GO_ON);
       ScrollScreen(NULL, SCROLL_GO_ON);
       FrameCounter++;
+
       DrawAllPlayers();
+
       BackToFront();
+      Delay(GAME_FRAME_DELAY);
     }
 
+    player->is_moving = FALSE;
+
+#if 0
     RemoveField(player->jx, player->jy);
+#endif
+
     DrawLevelField(player->jx, player->jy);
   }
 
+  Feld[x][y] = element;
   InitPlayerField(x, y, element, TRUE);
 
   if (player == local_player)
@@ -2076,7 +2114,7 @@ void RelocatePlayer(int x, int y, int element)
       ScrollLevel(dx, dy);
       DrawAllPlayers();
 
-      /* scroll in to steps of half tile size to make things smoother */
+      /* scroll in two steps of half tile size to make things smoother */
       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
       FlushDisplay();
       Delay(GAME_FRAME_DELAY);
@@ -2374,7 +2412,10 @@ void Explode(int ex, int ey, int phase, int mode)
       element = Feld[x][y] = Back[x][y];
     Back[x][y] = 0;
 
-    MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = ChangeDelay[x][y] = 0;
+    MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
+    ChangeDelay[x][y] = 0;
+    ChangePage[x][y] = -1;
+
     InitField(x, y, FALSE);
     if (CAN_MOVE(element))
       InitMovDir(x, y);
@@ -4236,6 +4277,11 @@ void StartMoving(int x, int y)
            element1 != EL_DRAGON && element2 != EL_DRAGON &&
            element1 != EL_FLAMES && element2 != EL_FLAMES)
        {
+#if 1
+         ResetGfxAnimation(x, y);
+         GfxAction[x][y] = ACTION_ATTACKING;
+#endif
+
          if (IS_PLAYER(x, y))
            DrawPlayerField(x, y);
          else
@@ -4249,6 +4295,7 @@ void StartMoving(int x, int y)
            Feld[newx1][newy1] = EL_FLAMES;
          if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
            Feld[newx2][newy2] = EL_FLAMES;
+
          return;
        }
       }
@@ -4459,10 +4506,12 @@ void ContinueMoving(int x, int y)
 
     /* copy element change control values to new field */
     ChangeDelay[newx][newy] = ChangeDelay[x][y];
+    ChangePage[newx][newy] = ChangePage[x][y];
     Changed[newx][newy] = Changed[x][y];
     ChangeEvent[newx][newy] = ChangeEvent[x][y];
 
     ChangeDelay[x][y] = 0;
+    ChangePage[x][y] = -1;
     Changed[x][y] = CE_BITMASK_DEFAULT;
     ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
 
@@ -4525,13 +4574,19 @@ void ContinueMoving(int x, int y)
        (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
       Impact(x, newy);
 
-    if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
-      CheckElementSideChange(newx, newy, element, direction, CE_COLLISION, -1);
-
 #if 1
     TestIfElementTouchesCustomElement(x, y);           /* for empty space */
 #endif
 
+#if 0
+    if (ChangePage[newx][newy] != -1)                  /* delayed change */
+      ChangeElement(newx, newy, ChangePage[newx][newy]);
+#endif
+
+    if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
+      CheckElementSideChange(newx, newy, Feld[newx][newy], direction,
+                            CE_COLLISION, -1);
+
     TestIfPlayerTouchesCustomElement(newx, newy);
     TestIfElementTouchesCustomElement(newx, newy);
   }
@@ -5094,7 +5149,10 @@ void CheckExitSP(int x, int y)
     return;
   }
 
-  Feld[x][y] = EL_SP_EXIT_OPEN;
+  if (AllPlayersGone)  /* do not re-open exit door closed after last player */
+    return;
+
+  Feld[x][y] = EL_SP_EXIT_OPENING;
 
   PlaySoundLevelNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
 }
@@ -5592,7 +5650,8 @@ static boolean ChangeElementNow(int x, int y, int element, int page)
 static void ChangeElement(int x, int y, int page)
 {
   int element = MovingOrBlocked2Element(x, y);
-  struct ElementChangeInfo *change = &element_info[element].change_page[page];
+  struct ElementInfo *ei = &element_info[element];
+  struct ElementChangeInfo *change = &ei->change_page[page];
 
 #if 0
 #ifdef DEBUG
@@ -5633,9 +5692,16 @@ static void ChangeElement(int x, int y, int page)
   }
   else                                 /* finish element change */
   {
+    if (ChangePage[x][y] != -1)                /* remember page from delayed change */
+    {
+      page = ChangePage[x][y];
+      ChangePage[x][y] = -1;
+    }
+
     if (IS_MOVING(x, y))               /* never change a running system ;-) */
     {
       ChangeDelay[x][y] = 1;           /* try change after next move step */
+      ChangePage[x][y] = page;         /* remember page to use for change */
 
       return;
     }
@@ -5672,7 +5738,8 @@ static boolean CheckTriggeredElementSideChange(int lx, int ly,
     {
       struct ElementChangeInfo *change = &element_info[element].change_page[j];
 
-      if (change->sides & trigger_side &&
+      if (change->can_change &&
+         change->sides & trigger_side &&
          change->trigger_element == trigger_element)
       {
        change_element = TRUE;
@@ -5931,6 +5998,16 @@ void GameActions()
     Changed[x][y] = CE_BITMASK_DEFAULT;
     ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
 
+#if DEBUG
+    if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
+    {
+      printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
+      printf("GameActions(): This should never happen!\n");
+
+      ChangePage[x][y] = -1;
+    }
+#endif
+
     Stop[x][y] = FALSE;
     if (JustStopped[x][y] > 0)
       JustStopped[x][y]--;
@@ -6007,7 +6084,13 @@ void GameActions()
     /* this may take place after moving, so 'element' may have changed */
     if (IS_CHANGING(x, y))
     {
+#if 0
+      ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
+                   element_info[element].event_page_nr[CE_DELAY]);
+#else
       ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
+#endif
+
       element = Feld[x][y];
       graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
     }
@@ -6425,7 +6508,7 @@ void ScrollLevel(int dx, int dy)
 
 static void CheckGravityMovement(struct PlayerInfo *player)
 {
-  if (level.gravity && !player->programmed_action)
+  if (game.gravity && !player->programmed_action)
   {
     int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
     int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
@@ -6538,7 +6621,7 @@ boolean MoveFigureOneStep(struct PlayerInfo *player,
 
   ScrollFigure(player, SCROLL_INIT);
 
-#if 1
+#if 0
   if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
   {
     CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
@@ -6720,6 +6803,44 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
 #if 1
     player->Switching = FALSE;
 #endif
+
+
+#if 1
+    {
+      static int change_sides[4][2] =
+      {
+       /* enter side           leave side */
+       { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
+       { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
+       { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
+       { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
+      };
+      int move_direction = player->MovDir;
+      int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
+      int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
+
+#if 1
+      if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
+      {
+       CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
+                                       leave_side, CE_OTHER_GETS_LEFT);
+       CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
+                              leave_side, CE_LEFT_BY_PLAYER, -1);
+      }
+
+      if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
+      {
+       CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
+                                       enter_side, CE_OTHER_GETS_ENTERED);
+       CheckElementSideChange(jx, jy, Feld[jx][jy],
+                              enter_side, CE_ENTERED_BY_PLAYER, -1);
+      }
+#endif
+
+    }
+#endif
+
+
   }
   else
   {
@@ -6794,13 +6915,14 @@ void ScrollFigure(struct PlayerInfo *player, int mode)
     player->last_jy = jy;
 
     if (Feld[jx][jy] == EL_EXIT_OPEN ||
-       Feld[jx][jy] == EL_SP_EXIT_OPEN)
+       Feld[jx][jy] == EL_SP_EXIT_OPEN ||
+       Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
     {
       DrawPlayer(player);      /* needed here only to cleanup last field */
       RemoveHero(player);
 
       if (local_player->friends_still_needed == 0 ||
-         Feld[jx][jy] == EL_SP_EXIT_OPEN)
+         IS_SP_ELEMENT(Feld[jx][jy]))
        player->LevelSolved = player->GameOver = TRUE;
     }
 
@@ -6935,11 +7057,28 @@ void TestIfElementTouchesCustomElement(int x, int y)
     { CH_SIDE_RIGHT,   CH_SIDE_LEFT    },      /* check right  */
     { CH_SIDE_BOTTOM,  CH_SIDE_TOP     }       /* check bottom */
   };
+  static int touch_dir[4] =
+  {
+    MV_LEFT | MV_RIGHT,
+    MV_UP   | MV_DOWN,
+    MV_UP   | MV_DOWN,
+    MV_LEFT | MV_RIGHT
+  };
   boolean change_center_element = FALSE;
   int center_element_change_page = 0;
-  int center_element = Feld[x][y];
+#if 0
+  int center_element = MovingOrBlocked2Element(x, y);
+#else
+  int center_element = Feld[x][y];     /* should always be non-moving! */
+#endif
   int i, j;
 
+#if 0
+  if (center_element == EL_CUSTOM_START + 244)
+    printf("::: checking element %d at %d,%d... [%d]\n",
+          center_element, x, y, Feld[x][y + 1]);
+#endif
+
 #if 0
   if (check_changing)  /* prevent this function from running into a loop */
     return;
@@ -6958,7 +7097,34 @@ void TestIfElementTouchesCustomElement(int x, int y)
     if (!IN_LEV_FIELD(xx, yy))
       continue;
 
-    border_element = Feld[xx][yy];
+#if 0
+    if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
+      border_element = Feld[xx][yy];
+    else if (MovDir[xx][yy] & touch_dir[i])    /* elements are touching */
+    {
+#if 0
+      printf("::: moving && touching...\n");
+#endif
+
+      border_element = MovingOrBlocked2Element(xx, yy);
+    }
+    else
+    {
+#if 0
+      printf("::: moving && NOT touching...\n");
+#endif
+
+      continue;                        /* center and border element do not touch */
+    }
+#else
+
+#if 0
+    border_element = MovingOrBlocked2Element(xx, yy);
+#else
+    border_element = Feld[xx][yy];     /* may be moving! */
+#endif
+
+#endif
 
     /* check for change of center element (but change it only once) */
     if (IS_CUSTOM_ELEMENT(center_element) &&
@@ -6970,7 +7136,8 @@ void TestIfElementTouchesCustomElement(int x, int y)
        struct ElementChangeInfo *change =
          &element_info[center_element].change_page[j];
 
-       if (change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
+       if (change->can_change &&
+           change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
            change->sides & border_side &&
            change->trigger_element == border_element)
        {
@@ -6991,10 +7158,16 @@ void TestIfElementTouchesCustomElement(int x, int y)
        struct ElementChangeInfo *change =
          &element_info[border_element].change_page[j];
 
-       if (change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
+       if (change->can_change &&
+           change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
            change->sides & center_side &&
            change->trigger_element == center_element)
        {
+#if 0
+         printf("::: changing border element %d at %d,%d\n",
+                border_element, xx, yy);
+#endif
+
          CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
                                 CE_OTHER_IS_TOUCHING, j);
          break;
@@ -7004,8 +7177,15 @@ void TestIfElementTouchesCustomElement(int x, int y)
   }
 
   if (change_center_element)
+  {
+#if 0
+    printf("::: changing center element %d at %d,%d\n",
+          center_element, x, y);
+#endif
+
     CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
                           CE_OTHER_IS_TOUCHING, center_element_change_page);
+  }
 
 #if 0
   check_changing = FALSE;
@@ -7597,7 +7777,7 @@ int DigField(struct PlayerInfo *player,
          element == EL_SP_GRAVITY_PORT_RIGHT ||
          element == EL_SP_GRAVITY_PORT_UP ||
          element == EL_SP_GRAVITY_PORT_DOWN)
-       level.gravity = !level.gravity;
+       game.gravity = !game.gravity;
 
       /* automatically move to the next field with double speed */
       player->programmed_action = move_direction;
@@ -7686,7 +7866,9 @@ int DigField(struct PlayerInfo *player,
          if (!player->key[element - EL_GATE_1_GRAY])
            return MF_NO_ACTION;
        }
-       else if (element == EL_EXIT_OPEN || element == EL_SP_EXIT_OPEN)
+       else if (element == EL_EXIT_OPEN ||
+                element == EL_SP_EXIT_OPEN ||
+                element == EL_SP_EXIT_OPENING)
        {
          sound_action = ACTION_PASSING;        /* player is passing exit */
        }