rnd-20040129-2-src
[rocksndiamonds.git] / src / game.c
index dfad329ef9c2b2e64d5df8fb4e1a4c6986edd595..7b225c5eac9621eb61e6ee2dc195b16d6a4b4c76 100644 (file)
 #endif
 #endif
 
+#define GROUP_NR(e)            ((e) - EL_GROUP_START)
 #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(e, nr)     (element_info[e].in_group[nr] == TRUE)
 #define IS_IN_GROUP_EL(e, ge)  (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
 
+#define IS_EQUAL_OR_IN_GROUP(e, ge)                                    \
+       (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
+
+#if 1
+#define CE_ENTER_FIELD_COND(e, x, y)                                   \
+               (!IS_PLAYER(x, y) &&                                    \
+                (Feld[x][y] == EL_ACID ||                              \
+                 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
+#else
 #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)))))
+#endif
 
 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
        ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
@@ -799,9 +810,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);
 
-       Feld[x][y] = group->element_resolved[random_pos];
+       if (group->choice_mode == ANIM_RANDOM)
+         gfx.anim_random_frame = last_anim_random_frame;
+
+       group->choice_pos++;
+
+       Feld[x][y] = group->element_resolved[element_pos];
 
        InitField(x, y, init_game);
       }
@@ -850,8 +874,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++)
@@ -1037,7 +1063,16 @@ static void InitGameEngine()
       {
        int trigger_element = ei->change_page[j].trigger_element;
 
-       trigger_events[trigger_element] |= ei->change_page[j].events;
+       if (IS_GROUP_ELEMENT(trigger_element))
+       {
+         struct ElementGroupInfo *group = element_info[trigger_element].group;
+
+         for (k = 0; k < group->num_elements_resolved; k++)
+           trigger_events[group->element_resolved[k]]
+             |= ei->change_page[j].events;
+       }
+       else
+         trigger_events[trigger_element] |= ei->change_page[j].events;
       }
     }
   }
@@ -1085,6 +1120,14 @@ static void InitGameEngine()
     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
   }
 
+  /* ---------- initialize move dig/leave ---------------------------------- */
+
+  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+  {
+    element_info[i].can_leave_element = FALSE;
+    element_info[i].can_leave_element_last = FALSE;
+  }
+
   /* ---------- initialize gem count --------------------------------------- */
 
   /* initialize gem count values for each element */
@@ -1378,7 +1421,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;
@@ -1392,7 +1437,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++)
     {
@@ -1423,6 +1468,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;
          }
@@ -1735,10 +1782,11 @@ void InitMovDir(int x, int y)
          move_direction_initial = MV_AUTOMATIC;
        }
 
-       if (move_direction_initial & MV_ANY_DIRECTION)
+       if (move_direction_initial == MV_RANDOM)
+         MovDir[x][y] = 1 << RND(4);
+       else 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 ||
+       else if (move_pattern == MV_ALL_DIRECTIONS ||
                 move_pattern == MV_TURNING_LEFT ||
                 move_pattern == MV_TURNING_RIGHT ||
                 move_pattern == MV_TURNING_LEFT_RIGHT ||
@@ -2470,17 +2518,17 @@ void Explode(int ex, int ey, int phase, int mode)
        switch(StorePlayer[ex][ey])
        {
          case EL_PLAYER_2:
-           Store[x][y] = EL_EMERALD_RED;
+           Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
            break;
          case EL_PLAYER_3:
-           Store[x][y] = EL_EMERALD;
+           Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
            break;
          case EL_PLAYER_4:
-           Store[x][y] = EL_EMERALD_PURPLE;
+           Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
            break;
          case EL_PLAYER_1:
          default:
-           Store[x][y] = EL_EMERALD_YELLOW;
+           Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
            break;
        }
 
@@ -2627,6 +2675,17 @@ void Explode(int ex, int ey, int phase, int mode)
     Store[x][y] = Store2[x][y] = 0;
     GfxElement[x][y] = EL_UNDEFINED;
 
+    /* player can escape from explosions and might therefore be still alive */
+    if (element >= EL_PLAYER_IS_EXPLODING_1 &&
+       element <= EL_PLAYER_IS_EXPLODING_4)
+      Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
+                   EL_EMPTY :
+                   element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
+                   element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
+                   element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
+                   EL_EMERALD_PURPLE);
+
+    /* restore probably existing indestructible background element */
     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
       element = Feld[x][y] = Back[x][y];
     Back[x][y] = 0;
@@ -2637,8 +2696,11 @@ void Explode(int ex, int ey, int phase, int mode)
     ChangePage[x][y] = -1;
 
     InitField(x, y, FALSE);
+#if 1
+    /* !!! not needed !!! */
     if (CAN_MOVE(element))
       InitMovDir(x, y);
+#endif
     DrawLevelField(x, y);
 
     TestIfElementTouchesCustomElement(x, y);
@@ -3304,11 +3366,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;
@@ -4731,6 +4795,8 @@ void StartMoving(int x, int y)
 
 )
     {
+      int new_element = Feld[newx][newy];
+
 #if 0
       printf("::: '%s' digs '%s' [%d]\n",
             element_info[element].token_name,
@@ -4740,8 +4806,9 @@ void StartMoving(int x, int y)
 
       if (!IS_FREE(newx, newy))
       {
-       int new_element = Feld[newx][newy];
-       int sound;
+       int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
+                     IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
+                     ACTION_BREAKING);
 
        /* no element can dig solid indestructible elements */
        if (IS_INDESTRUCTIBLE(new_element) &&
@@ -4766,13 +4833,12 @@ void StartMoving(int x, int y)
          DrawLevelField(newx, newy);
        }
 
-       sound = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
-                IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
-                ACTION_BREAKING);
-
-       PlayLevelSoundAction(x, y, sound);
+       PlayLevelSoundAction(x, y, action);
       }
 
+      if (new_element == element_info[element].move_enter_element)
+       element_info[element].can_leave_element = TRUE;
+
       if (move_pattern & MV_MAZE_RUNNER_STYLE)
       {
        RunnerVisit[x][y] = FrameCounter;
@@ -4953,6 +5019,7 @@ void StartMoving(int x, int y)
 void ContinueMoving(int x, int y)
 {
   int element = Feld[x][y];
+  struct ElementInfo *ei = &element_info[element];
   int direction = MovDir[x][y];
   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
@@ -5070,22 +5137,21 @@ 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))
+  /* 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 ||
+       ei->can_leave_element_last))
   {
-    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);
+    Feld[x][y] = ei->move_leave_element;
+    InitField(x, y, FALSE);
 
-      if (GFX_CRUMBLED(new_element))
-       DrawLevelFieldCrumbledSandNeighbours(x, y);
-    }
+    if (GFX_CRUMBLED(Feld[x][y]))
+      DrawLevelFieldCrumbledSandNeighbours(x, y);
   }
+
+  ei->can_leave_element_last = ei->can_leave_element;
+  ei->can_leave_element = FALSE;
 #endif
 
 #if 0
@@ -5147,7 +5213,7 @@ void ContinueMoving(int x, int y)
     Impact(x, newy);
 
 #if 1
-  TestIfElementTouchesCustomElement(x, y);             /* for empty space */
+  TestIfElementTouchesCustomElement(x, y);     /* empty or new element */
 #endif
 
 #if 0
@@ -6400,7 +6466,12 @@ static boolean CheckTriggeredElementSideChange(int lx, int ly,
          change->events & CH_EVENT_BIT(trigger_event) &&
 #endif
          change->sides & trigger_side &&
-         change->trigger_element == trigger_element)
+#if 1
+         IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)
+#else
+         change->trigger_element == trigger_element
+#endif
+         )
       {
 #if 0
        if (!(change->events & CH_EVENT_BIT(trigger_event)))
@@ -7479,6 +7550,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)
@@ -7498,7 +7601,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 &&
@@ -8129,7 +8234,12 @@ void TestIfElementTouchesCustomElement(int x, int y)
        if (change->can_change &&
            change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
            change->sides & border_side &&
-           change->trigger_element == border_element)
+#if 1
+           IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
+#else
+           change->trigger_element == border_element
+#endif
+           )
        {
          change_center_element = TRUE;
          center_element_change_page = j;
@@ -8151,7 +8261,12 @@ void TestIfElementTouchesCustomElement(int x, int y)
        if (change->can_change &&
            change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
            change->sides & center_side &&
-           change->trigger_element == center_element)
+#if 1
+           IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
+#else
+           change->trigger_element == center_element
+#endif
+           )
        {
          CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
                                 CE_OTHER_IS_TOUCHING, j);
@@ -8231,7 +8346,13 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
          if (change->can_change &&
              change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
              change->sides & touched_side &&
-             change->trigger_element == touched_element)
+         
+#if 1
+             IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
+#else
+             change->trigger_element == touched_element
+#endif
+             )
          {
            CheckElementSideChange(x, y, hitting_element,
                                   CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
@@ -8251,7 +8372,12 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
          if (change->can_change &&
              change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
              change->sides & hitting_side &&
-             change->trigger_element == hitting_element)
+#if 1
+             IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
+#else
+             change->trigger_element == hitting_element
+#endif
+             )
          {
            CheckElementSideChange(hitx, hity, touched_element,
                                   CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
@@ -8680,6 +8806,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 &&
@@ -8703,6 +8833,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 ||