rnd-20040205-1-src
[rocksndiamonds.git] / src / game.c
index bab2a31211f966ac2cfa56d2328e64da733fd4f6..f5dc4186379d4a9063acd9b4441139d286ba1797 100644 (file)
@@ -606,6 +606,10 @@ static void InitPlayerField(int x, int y, int element, boolean init_game)
 
     player->present = TRUE;
 
+    player->block_last_field = (element == EL_SP_MURPHY ?
+                               level.sp_block_last_field :
+                               level.block_last_field);
+
     if (!options.network || player->connected)
     {
       player->active = TRUE;
@@ -810,9 +814,22 @@ static void InitField(int x, int y, boolean init_game)
       else if (IS_GROUP_ELEMENT(element))
       {
        struct ElementGroupInfo *group = element_info[element].group;
-       int random_pos = RND(group->num_elements_resolved);
+       int last_anim_random_frame = gfx.anim_random_frame;
+       int element_pos;
+
+       if (group->choice_mode == ANIM_RANDOM)
+         gfx.anim_random_frame = RND(group->num_elements_resolved);
+
+       element_pos = getAnimationFrame(group->num_elements_resolved, 1,
+                                       group->choice_mode, 0,
+                                       group->choice_pos);
+
+       if (group->choice_mode == ANIM_RANDOM)
+         gfx.anim_random_frame = last_anim_random_frame;
+
+       group->choice_pos++;
 
-       Feld[x][y] = group->element_resolved[random_pos];
+       Feld[x][y] = group->element_resolved[element_pos];
 
        InitField(x, y, init_game);
       }
@@ -853,7 +870,7 @@ static void resolve_group_element(int group_element, int recursion_depth)
          group_element - EL_GROUP_START + 1);
 
     /* replace element which caused too deep recursion by question mark */
-    group->element_resolved[group->num_elements_resolved++] = EL_CHAR_QUESTION;
+    group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
 
     return;
   }
@@ -861,8 +878,10 @@ static void resolve_group_element(int group_element, int recursion_depth)
   if (recursion_depth == 0)                    /* initialization */
   {
     group = element_info[group_element].group;
-    group->num_elements_resolved = 0;
     group_nr = group_element - EL_GROUP_START;
+
+    group->num_elements_resolved = 0;
+    group->choice_pos = 0;
   }
 
   for (i = 0; i < actual_group->num_elements; i++)
@@ -1090,6 +1109,19 @@ static void InitGameEngine()
     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
   }
 
+  /* set push delay value for Supaplex elements for newer engine versions */
+  if (game.engine_version >= VERSION_IDENT(3,0,9,0))
+  {
+    for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+    {
+      if (IS_SP_ELEMENT(i))
+      {
+       element_info[i].push_delay_fixed  = 6;
+       element_info[i].push_delay_random = 0;
+      }
+    }
+  }
+
   /* ---------- initialize move stepsize ----------------------------------- */
 
   /* initialize move stepsize values to default */
@@ -1195,6 +1227,8 @@ void InitGame()
 
     player->use_murphy_graphic = FALSE;
 
+    player->block_last_field = FALSE;
+
     player->actual_frame_counter = 0;
 
     player->step_counter = 0;
@@ -1406,7 +1440,9 @@ void InitGame()
        {
          player->present = TRUE;
          player->active = TRUE;
+
          some_player->present = FALSE;
+         some_player->active = FALSE;
 
          StorePlayer[jx][jy] = player->element_nr;
          player->jx = player->last_jx = jx;
@@ -1420,7 +1456,7 @@ void InitGame()
 
   if (tape.playing)
   {
-    /* when playing a tape, eliminate all players who do not participate */
+    /* when playing a tape, eliminate all players which do not participate */
 
     for (i = 0; i < MAX_PLAYERS; i++)
     {
@@ -1451,6 +1487,8 @@ void InitGame()
            int jx = player->jx, jy = player->jy;
 
            player->active = FALSE;
+           player->present = FALSE;
+
            StorePlayer[jx][jy] = 0;
            Feld[jx][jy] = EL_EMPTY;
          }
@@ -1755,15 +1793,15 @@ void InitMovDir(int x, int y)
        int move_direction_initial = ei->move_direction_initial;
        int move_pattern = ei->move_pattern;
 
-       if (move_direction_initial == MV_PREVIOUS)
+       if (move_direction_initial == MV_START_PREVIOUS)
        {
          if (MovDir[x][y] != MV_NO_MOVING)
            return;
 
-         move_direction_initial = MV_AUTOMATIC;
+         move_direction_initial = MV_START_AUTOMATIC;
        }
 
-       if (move_direction_initial == MV_RANDOM)
+       if (move_direction_initial == MV_START_RANDOM)
          MovDir[x][y] = 1 << RND(4);
        else if (move_direction_initial & MV_ANY_DIRECTION)
          MovDir[x][y] = move_direction_initial;
@@ -2300,6 +2338,10 @@ void CheckDynamite(int x, int y)
 void RelocatePlayer(int x, int y, int element)
 {
   struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
+  boolean ffwd_delay = (tape.playing && tape.fast_forward);
+  boolean no_delay = (tape.index_search);
+  int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
+  int wait_delay_value = (no_delay ? 0 : frame_delay_value);
 
   if (player->GameOver)                /* do not reanimate dead player */
     return;
@@ -2320,7 +2362,7 @@ void RelocatePlayer(int x, int y, int element)
       DrawPlayer(player);
 
       BackToFront();
-      Delay(GAME_FRAME_DELAY);
+      Delay(wait_delay_value);
     }
 
     DrawPlayer(player);                /* needed here only to cleanup last field */
@@ -2364,11 +2406,11 @@ void RelocatePlayer(int x, int y, int element)
       /* 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);
+      Delay(wait_delay_value);
 
       /* scroll second step to align at full tile size */
       BackToFront();
-      Delay(GAME_FRAME_DELAY);
+      Delay(wait_delay_value);
     }
   }
 }
@@ -3347,11 +3389,13 @@ void Impact(int x, int y)
        return;
       }
     }
-    else if ((element == EL_SP_INFOTRON ||
-             element == EL_SP_ZONK) &&
-            (smashed == EL_SP_SNIKSNAK ||
-             smashed == EL_SP_ELECTRON ||
-             smashed == EL_SP_DISK_ORANGE))
+    else if (((element == EL_SP_INFOTRON ||
+              element == EL_SP_ZONK) &&
+             (smashed == EL_SP_SNIKSNAK ||
+              smashed == EL_SP_ELECTRON ||
+              smashed == EL_SP_DISK_ORANGE)) ||
+            (element == EL_SP_INFOTRON &&
+             smashed == EL_SP_DISK_YELLOW))
     {
       Bang(x, y + 1);
       return;
@@ -5116,6 +5160,7 @@ void ContinueMoving(int x, int y)
   ResetGfxAnimation(x, y);     /* reset animation values for old field */
 
 #if 1
+  /* some elements can leave other elements behind after moving */
   if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
       ei->move_leave_element != EL_EMPTY &&
       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
@@ -6202,7 +6247,7 @@ 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)
+  if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
     MovDir[x][y] = previous_move_direction;
 
   InitField(x, y, FALSE);
@@ -7528,6 +7573,38 @@ void ScrollLevel(int dx, int dy)
   redraw_mask |= REDRAW_FIELD;
 }
 
+static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
+{
+  int nextx = x + dx, nexty = y + dy;
+  int element = Feld[x][y];
+
+  if ((dx == -1 &&
+       element != EL_SP_PORT_LEFT &&
+       element != EL_SP_GRAVITY_PORT_LEFT &&
+       element != EL_SP_PORT_HORIZONTAL &&
+       element != EL_SP_PORT_ANY) ||
+      (dx == +1 &&
+       element != EL_SP_PORT_RIGHT &&
+       element != EL_SP_GRAVITY_PORT_RIGHT &&
+       element != EL_SP_PORT_HORIZONTAL &&
+       element != EL_SP_PORT_ANY) ||
+      (dy == -1 &&
+       element != EL_SP_PORT_UP &&
+       element != EL_SP_GRAVITY_PORT_UP &&
+       element != EL_SP_PORT_VERTICAL &&
+       element != EL_SP_PORT_ANY) ||
+      (dy == +1 &&
+       element != EL_SP_PORT_DOWN &&
+       element != EL_SP_GRAVITY_PORT_DOWN &&
+       element != EL_SP_PORT_VERTICAL &&
+       element != EL_SP_PORT_ANY) ||
+      !IN_LEV_FIELD(nextx, nexty) ||
+      !IS_FREE(nextx, nexty))
+    return FALSE;
+
+  return TRUE;
+}
+
 static void CheckGravityMovement(struct PlayerInfo *player)
 {
   if (game.gravity && !player->programmed_action)
@@ -7547,7 +7624,9 @@ static void CheckGravityMovement(struct PlayerInfo *player)
     boolean player_is_moving_to_valid_field =
       (IN_LEV_FIELD(new_jx, new_jy) &&
        (Feld[new_jx][new_jy] == EL_SP_BASE ||
-       Feld[new_jx][new_jy] == EL_SAND));
+       Feld[new_jx][new_jy] == EL_SAND ||
+       (IS_SP_PORT(Feld[new_jx][new_jy]) &&
+        canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
     /* !!! extend EL_SAND to anything diggable !!! */
 
     if (field_under_player_is_free &&
@@ -7935,6 +8014,7 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
 #if 0
     DrawPlayer(player);
 #endif
+
     return;
   }
   else if (!FrameReached(&player->actual_frame_counter, 1))
@@ -7943,7 +8023,8 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
 
-  if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
+  if (!player->block_last_field &&
+      Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
     Feld[last_jx][last_jy] = EL_EMPTY;
 
   /* before DrawPlayer() to draw correct player graphic for this case */
@@ -7981,6 +8062,10 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
     }
 #endif
 
+    if (player->block_last_field &&
+       Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
+      Feld[last_jx][last_jy] = EL_EMPTY;
+
     player->last_jx = jx;
     player->last_jy = jy;
 
@@ -8750,6 +8835,10 @@ int DigField(struct PlayerInfo *player,
     case EL_SP_GRAVITY_PORT_RIGHT:
     case EL_SP_GRAVITY_PORT_UP:
     case EL_SP_GRAVITY_PORT_DOWN:
+#if 1
+      if (!canEnterSupaplexPort(x, y, dx, dy))
+       return MF_NO_ACTION;
+#else
       if ((dx == -1 &&
           element != EL_SP_PORT_LEFT &&
           element != EL_SP_GRAVITY_PORT_LEFT &&
@@ -8773,6 +8862,7 @@ int DigField(struct PlayerInfo *player,
          !IN_LEV_FIELD(nextx, nexty) ||
          !IS_FREE(nextx, nexty))
        return MF_NO_ACTION;
+#endif
 
       if (element == EL_SP_GRAVITY_PORT_LEFT ||
          element == EL_SP_GRAVITY_PORT_RIGHT ||
@@ -9437,6 +9527,11 @@ boolean DropElement(struct PlayerInfo *player)
 
     PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
 
+#if 1
+    /* needed if previous element just changed to "empty" in the last frame */
+    Changed[jx][jy] = 0;               /* allow another change */
+#endif
+
     CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
     CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
 
@@ -9474,7 +9569,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_AUTOMATIC)
+    if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
       MovDir[jx][jy] = player->MovDir;
 
     direction = MovDir[jx][jy];
@@ -9494,7 +9589,7 @@ boolean DropElement(struct PlayerInfo *player)
     }
     else
     {
-      Changed[jx][jy] = 0;            /* allow another change */
+      Changed[jx][jy] = 0;             /* allow another change */
 
 #if 1
       TestIfElementHitsCustomElement(jx, jy, direction);