rnd-20030829-R-src
[rocksndiamonds.git] / src / game.c
index 32311aa5f492bdf4c5e4d5a540faf6a6130c3345..7b3017e95a7f2864f62b877f913e4bebceccb933 100644 (file)
@@ -427,7 +427,7 @@ static unsigned long trigger_events[MAX_NUM_ELEMENTS];
 void GetPlayerConfig()
 {
   if (!audio.sound_available)
-    setup.sound = FALSE;
+    setup.sound_simple = FALSE;
 
   if (!audio.loops_available)
     setup.sound_loops = FALSE;
@@ -438,7 +438,7 @@ void GetPlayerConfig()
   if (!video.fullscreen_available)
     setup.fullscreen = FALSE;
 
-  setup.sound_simple = setup.sound;
+  setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
 
   SetAudioMode(setup.sound);
   InitJoysticks();
@@ -495,62 +495,71 @@ static int getBeltDirFromBeltSwitchElement(int element)
   return belt_move_dir[belt_dir_nr];
 }
 
-static void InitField(int x, int y, boolean init_game)
+static void InitPlayerField(int x, int y, int element, boolean init_game)
 {
-  int element = Feld[x][y];
-
-  switch (element)
+  if (element == EL_SP_MURPHY)
   {
-    case EL_SP_MURPHY:
-      if (init_game)
+    if (init_game)
+    {
+      if (stored_player[0].present)
       {
-       if (stored_player[0].present)
-       {
-         Feld[x][y] = EL_SP_MURPHY_CLONE;
-         break;
-       }
-       else
-       {
-         stored_player[0].use_murphy_graphic = TRUE;
-       }
+       Feld[x][y] = EL_SP_MURPHY_CLONE;
 
-       Feld[x][y] = EL_PLAYER_1;
+       return;
       }
-      /* no break! */
-    case EL_PLAYER_1:
-    case EL_PLAYER_2:
-    case EL_PLAYER_3:
-    case EL_PLAYER_4:
-      if (init_game)
+      else
       {
-       struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
-       int jx = player->jx, jy = player->jy;
+       stored_player[0].use_murphy_graphic = TRUE;
+      }
 
-       player->present = TRUE;
+      Feld[x][y] = EL_PLAYER_1;
+    }
+  }
 
-       if (!options.network || player->connected)
-       {
-         player->active = TRUE;
+  if (init_game)
+  {
+    struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
+    int jx = player->jx, jy = player->jy;
 
-         /* remove potentially duplicate players */
-         if (StorePlayer[jx][jy] == Feld[x][y])
-           StorePlayer[jx][jy] = 0;
+    player->present = TRUE;
 
-         StorePlayer[x][y] = Feld[x][y];
+    if (!options.network || player->connected)
+    {
+      player->active = TRUE;
 
-         if (options.debug)
-         {
-           printf("Player %d activated.\n", player->element_nr);
-           printf("[Local player is %d and currently %s.]\n",
-                  local_player->element_nr,
-                  local_player->active ? "active" : "not active");
-         }
-       }
+      /* remove potentially duplicate players */
+      if (StorePlayer[jx][jy] == Feld[x][y])
+       StorePlayer[jx][jy] = 0;
 
-       Feld[x][y] = EL_EMPTY;
-       player->jx = player->last_jx = x;
-       player->jy = player->last_jy = y;
+      StorePlayer[x][y] = Feld[x][y];
+
+      if (options.debug)
+      {
+       printf("Player %d activated.\n", player->element_nr);
+       printf("[Local player is %d and currently %s.]\n",
+              local_player->element_nr,
+              local_player->active ? "active" : "not active");
       }
+    }
+
+    Feld[x][y] = EL_EMPTY;
+    player->jx = player->last_jx = x;
+    player->jy = player->last_jy = y;
+  }
+}
+
+static void InitField(int x, int y, boolean init_game)
+{
+  int element = Feld[x][y];
+
+  switch (element)
+  {
+    case EL_SP_MURPHY:
+    case EL_PLAYER_1:
+    case EL_PLAYER_2:
+    case EL_PLAYER_3:
+    case EL_PLAYER_4:
+      InitPlayerField(x, y, element, init_game);
       break;
 
     case EL_STONEBLOCK:
@@ -1207,16 +1216,101 @@ void InitGame()
   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
 
-  scroll_x = SBX_Left;
-  scroll_y = SBY_Upper;
-  if (local_player->jx >= SBX_Left + MIDPOSX)
-    scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
-               local_player->jx - MIDPOSX :
-               SBX_Right);
-  if (local_player->jy >= SBY_Upper + MIDPOSY)
-    scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
-               local_player->jy - MIDPOSY :
-               SBY_Lower);
+  /* if local player not found, look for custom element that might create
+     the player (make some assumptions about the right custom element) */
+  if (!local_player->present)
+  {
+    int start_x = 0, start_y = 0;
+    int found_rating = 0;
+    int found_element = EL_UNDEFINED;
+
+    for(y=0; y < lev_fieldy; y++) for(x=0; x < lev_fieldx; x++)
+    {
+      int element = Feld[x][y];
+      int content;
+      int xx, yy;
+      boolean is_player;
+
+      if (!IS_CUSTOM_ELEMENT(element))
+       continue;
+
+      if (CAN_CHANGE(element))
+      {
+       content = element_info[element].change.target_element;
+       is_player = ELEM_IS_PLAYER(content);
+
+       if (is_player && (found_rating < 3 || element < found_element))
+       {
+         start_x = x;
+         start_y = y;
+
+         found_rating = 3;
+         found_element = element;
+       }
+      }
+
+      for(yy=0; yy < 3; yy++) for(xx=0; xx < 3; xx++)
+      {
+       content = element_info[element].content[xx][yy];
+       is_player = ELEM_IS_PLAYER(content);
+
+       if (is_player && (found_rating < 2 || element < found_element))
+       {
+         start_x = x + xx - 1;
+         start_y = y + yy - 1;
+
+         found_rating = 2;
+         found_element = element;
+       }
+
+       if (!CAN_CHANGE(element))
+         continue;
+
+       content = element_info[element].change.content[xx][yy];
+       is_player = ELEM_IS_PLAYER(content);
+
+       if (is_player && (found_rating < 1 || element < found_element))
+       {
+         start_x = x + xx - 1;
+         start_y = y + yy - 1;
+
+         found_rating = 1;
+         found_element = element;
+       }
+      }
+    }
+
+    scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
+               start_x > SBX_Right + MIDPOSX ? SBX_Right :
+               start_x - MIDPOSX);
+
+    scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
+               start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
+               start_y - MIDPOSY);
+  }
+  else
+  {
+#if 1
+    scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
+               local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
+               local_player->jx - MIDPOSX);
+
+    scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
+               local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
+               local_player->jy - MIDPOSY);
+#else
+    scroll_x = SBX_Left;
+    scroll_y = SBY_Upper;
+    if (local_player->jx >= SBX_Left + MIDPOSX)
+      scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
+                 local_player->jx - MIDPOSX :
+                 SBX_Right);
+    if (local_player->jy >= SBY_Upper + MIDPOSY)
+      scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
+                 local_player->jy - MIDPOSY :
+                 SBY_Lower);
+#endif
+  }
 
   CloseDoor(DOOR_CLOSE_1);
 
@@ -1863,6 +1957,68 @@ void CheckDynamite(int x, int y)
   Bang(x, y);
 }
 
+void RelocatePlayer(int x, int y, int element)
+{
+  struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
+
+  if (player->present)
+  {
+    while (player->MovPos)
+    {
+      ScrollFigure(player, SCROLL_GO_ON);
+      ScrollScreen(NULL, SCROLL_GO_ON);
+      FrameCounter++;
+      DrawAllPlayers();
+      BackToFront();
+    }
+
+    RemoveField(player->jx, player->jy);
+    DrawLevelField(player->jx, player->jy);
+  }
+
+  InitPlayerField(x, y, element, TRUE);
+
+  if (player == local_player)
+  {
+    int scroll_xx = -999, scroll_yy = -999;
+
+    while (scroll_xx != scroll_x || scroll_yy != scroll_y)
+    {
+      int dx = 0, dy = 0;
+      int fx = FX, fy = FY;
+
+      scroll_xx = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
+                  local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
+                  local_player->jx - MIDPOSX);
+
+      scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
+                  local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
+                  local_player->jy - MIDPOSY);
+
+      dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
+      dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
+
+      scroll_x -= dx;
+      scroll_y -= dy;
+
+      fx += dx * TILEX / 2;
+      fy += dy * TILEY / 2;
+
+      ScrollLevel(dx, dy);
+      DrawAllPlayers();
+
+      /* scroll in to steps of half tile size to make things smoother */
+      BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
+      FlushDisplay();
+      Delay(GAME_FRAME_DELAY);
+
+      /* scroll second step to align at full tile size */
+      BackToFront();
+      Delay(GAME_FRAME_DELAY);
+    }
+  }
+}
+
 void Explode(int ex, int ey, int phase, int mode)
 {
   int x, y;
@@ -2142,6 +2298,9 @@ void Explode(int ex, int ey, int phase, int mode)
 
     if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
       StorePlayer[x][y] = 0;
+
+    if (ELEM_IS_PLAYER(element))
+      RelocatePlayer(x, y, element);
   }
   else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
   {
@@ -2290,7 +2449,10 @@ void Bang(int x, int y)
        Explode(x, y, EX_PHASE_START, EX_CENTER);
       break;
     default:
-      Explode(x, y, EX_PHASE_START, EX_NORMAL);
+      if (CAN_EXPLODE_1X1(element))
+       Explode(x, y, EX_PHASE_START, EX_CENTER);
+      else
+       Explode(x, y, EX_PHASE_START, EX_NORMAL);
       break;
   }
 
@@ -3458,7 +3620,8 @@ void StartMoving(int x, int y)
 
   if (CAN_FALL(element) && y < lev_fieldy - 1)
   {
-    if ((x>0 && IS_PLAYER(x-1, y)) || (x<lev_fieldx-1 && IS_PLAYER(x+1, y)))
+    if ((x > 0 && IS_PLAYER(x - 1, y)) ||
+       (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
       if (JustBeingPushed(x, y))
        return;
 
@@ -3859,7 +4022,8 @@ void StartMoving(int x, int y)
 
     Moving2Blocked(x, y, &newx, &newy);        /* get next screen position */
 
-    if (DONT_COLLIDE_WITH(element) && IS_PLAYER(newx, newy) &&
+    if (DONT_COLLIDE_WITH(element) &&
+       IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
        !PLAYER_PROTECTED(newx, newy))
     {
 #if 1
@@ -5148,13 +5312,14 @@ static void ChangeActiveTrap(int x, int y)
 
 static void ChangeElementNowExt(int x, int y, int target_element)
 {
-#if 0  /* !!! let the player exacpe from a suddenly unaccessible element */
-  if (IS_PLAYER(x, y) && !IS_ACCESSIBLE(target_element))
+  /* 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) &&
+      IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
   {
     Bang(x, y);
     return;
   }
-#endif
 
   RemoveField(x, y);
   Feld[x][y] = target_element;
@@ -5174,18 +5339,30 @@ static void ChangeElementNowExt(int x, int y, int target_element)
   TestIfBadThingTouchesHero(x, y);
   TestIfPlayerTouchesCustomElement(x, y);
   TestIfElementTouchesCustomElement(x, y);
+
+  if (ELEM_IS_PLAYER(target_element))
+    RelocatePlayer(x, y, target_element);
 }
 
 static void ChangeElementNow(int x, int y, int element)
 {
   struct ElementChangeInfo *change = &element_info[element].change;
 
+#if 0
+  if (element >= EL_CUSTOM_START + 17 && element <= EL_CUSTOM_START + 39)
+    printf("::: changing... [%d]\n", FrameCounter);
+#endif
+
+#if 0
   /* prevent CheckTriggeredElementChange() from looping */
   Changing[x][y] = TRUE;
+#endif
 
   CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
 
+#if 0
   Changing[x][y] = FALSE;
+#endif
 
   if (change->explode)
   {
@@ -5376,6 +5553,10 @@ static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
   if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
     return FALSE;
 
+  /* prevent this function from running into a loop */
+  if (trigger_event == CE_OTHER_IS_CHANGING)
+    Changing[lx][ly] = TRUE;
+
   for (i=0; i<MAX_NUM_ELEMENTS; i++)
   {
     if (!CAN_CHANGE(i) || !HAS_CHANGE_EVENT(i, trigger_event) ||
@@ -5394,10 +5575,16 @@ static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
       {
        ChangeDelay[x][y] = 1;
        ChangeElement(x, y);
+
+       Changing[x][y] = TRUE;  /* do not change just changed elements */
       }
     }
   }
 
+  /* reset change prevention array */
+  for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
+    Changing[x][y] = FALSE;
+
   return TRUE;
 }
 
@@ -6067,24 +6254,24 @@ void ScrollLevel(int dx, int dy)
   int x, y;
 
   BlitBitmap(drawto_field, drawto_field,
-            FX + TILEX*(dx == -1) - softscroll_offset,
-            FY + TILEY*(dy == -1) - softscroll_offset,
-            SXSIZE - TILEX*(dx!=0) + 2*softscroll_offset,
-            SYSIZE - TILEY*(dy!=0) + 2*softscroll_offset,
-            FX + TILEX*(dx == 1) - softscroll_offset,
-            FY + TILEY*(dy == 1) - softscroll_offset);
+            FX + TILEX * (dx == -1) - softscroll_offset,
+            FY + TILEY * (dy == -1) - softscroll_offset,
+            SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
+            SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
+            FX + TILEX * (dx == 1) - softscroll_offset,
+            FY + TILEY * (dy == 1) - softscroll_offset);
 
   if (dx)
   {
     x = (dx == 1 ? BX1 : BX2);
-    for (y=BY1; y<=BY2; y++)
+    for (y=BY1; y <= BY2; y++)
       DrawScreenField(x, y);
   }
 
   if (dy)
   {
     y = (dy == 1 ? BY1 : BY2);
-    for (x=BX1; x<=BX2; x++)
+    for (x=BX1; x <= BX2; x++)
       DrawScreenField(x, y);
   }
 
@@ -6176,6 +6363,10 @@ boolean MoveFigureOneStep(struct PlayerInfo *player,
   if (can_move != MF_MOVING)
     return can_move;
 
+  /* check if DigField() has caused relocation of the player */
+  if (player->jx != jx || player->jy != jy)
+    return MF_NO_ACTION;
+
   StorePlayer[jx][jy] = 0;
   player->last_jx = jx;
   player->last_jy = jy;
@@ -6345,6 +6536,9 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
 
     player->last_move_dir = player->MovDir;
     player->is_moving = TRUE;
+#if 1
+    player->snapped = FALSE;
+#endif
   }
   else
   {
@@ -6421,6 +6615,7 @@ void ScrollFigure(struct PlayerInfo *player, int mode)
     if (Feld[jx][jy] == EL_EXIT_OPEN ||
        Feld[jx][jy] == EL_SP_EXIT_OPEN)
     {
+      DrawPlayer(player);      /* needed here only to cleanup last field */
       RemoveHero(player);
 
       if (local_player->friends_still_needed == 0 ||
@@ -7463,8 +7658,15 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
     {
       player->is_digging = FALSE;
       player->is_collecting = FALSE;
+#if 1
+      player->is_moving = FALSE;
+#endif
     }
 
+#if 0
+    printf("::: trying to snap...\n");
+#endif
+
     return FALSE;
   }
 
@@ -7473,12 +7675,25 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
 
   player->MovDir = snap_direction;
 
+#if 1
+  player->is_digging = FALSE;
+  player->is_collecting = FALSE;
+#if 1
+  player->is_moving = FALSE;
+#endif
+#endif
+
   if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
     return FALSE;
 
   player->snapped = TRUE;
+#if 1
   player->is_digging = FALSE;
   player->is_collecting = FALSE;
+#if 1
+  player->is_moving = FALSE;
+#endif
+#endif
 
   DrawLevelField(x, y);
   BackToFront();