rnd-20061206-1-src
[rocksndiamonds.git] / src / game.c
index f0875a7a1d49fefefeb72cd8cad48bfe3a2b4366..3f3e6317d9629e25c395e863fc4ea8babdfe51bd 100644 (file)
@@ -1845,6 +1845,7 @@ void InitGame()
 
     player->present = FALSE;
     player->active = FALSE;
+    player->killed = FALSE;
 
     player->action = 0;
     player->effective_action = 0;
@@ -2734,35 +2735,38 @@ void GameWon()
       DrawGameValue_Score(score);
     }
 
-    if (ExitX >= 0 && ExitY >= 0)      /* local player has left the level */
+    if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
     {
-      /* close exit door after last player */
-      if (AllPlayersGone &&
-         (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
-          Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
+      if (ExitX >= 0 && ExitY >= 0)    /* local player has left the level */
       {
-       int element = Feld[ExitX][ExitY];
-
-       Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
-                             EL_SP_EXIT_CLOSING);
+       /* close exit door after last player */
+       if (AllPlayersGone &&
+           (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
+            Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
+       {
+         int element = Feld[ExitX][ExitY];
 
-       PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
-      }
+         Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
+                               EL_SP_EXIT_CLOSING);
 
-      /* player disappears */
-      DrawLevelField(ExitX, ExitY);
-    }
+         PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
+       }
 
-    for (i = 0; i < MAX_PLAYERS; i++)
-    {
-      struct PlayerInfo *player = &stored_player[i];
+       /* player disappears */
+       DrawLevelField(ExitX, ExitY);
+      }
 
-      if (player->present)
+      for (i = 0; i < MAX_PLAYERS; i++)
       {
-       RemovePlayer(player);
+       struct PlayerInfo *player = &stored_player[i];
 
-       /* player disappears */
-       DrawLevelField(player->jx, player->jy);
+       if (player->present)
+       {
+         RemovePlayer(player);
+
+         /* player disappears */
+         DrawLevelField(player->jx, player->jy);
+       }
       }
     }
 
@@ -3324,8 +3328,8 @@ static void setScreenCenteredToAllPlayers(int *sx, int *sy)
   *sy = (sy1 + sy2) / 2;
 }
 
-void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
-                       boolean quick_relocation)
+void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
+                       boolean center_screen, boolean quick_relocation)
 {
   boolean ffwd_delay = (tape.playing && tape.fast_forward);
   boolean no_delay = (tape.warp_forward);
@@ -3338,13 +3342,39 @@ void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
 
     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
     {
-      scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
-                 x > SBX_Right + MIDPOSX ? SBX_Right :
-                 x - MIDPOSX);
+      if (center_screen)
+      {
+       scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
+                   x > SBX_Right + MIDPOSX ? SBX_Right :
+                   x - MIDPOSX);
+
+       scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
+                   y > SBY_Lower + MIDPOSY ? SBY_Lower :
+                   y - MIDPOSY);
+      }
+      else
+      {
+       /* quick relocation (without scrolling), but do not center screen */
+
+       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
+                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
+                              old_x - MIDPOSX);
 
-      scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
-                 y > SBY_Lower + MIDPOSY ? SBY_Lower :
-                 y - MIDPOSY);
+       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
+                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
+                              old_y - MIDPOSY);
+
+       int offset_x = x + (scroll_x - center_scroll_x);
+       int offset_y = y + (scroll_y - center_scroll_y);
+
+       scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
+                   offset_x > SBX_Right + MIDPOSX ? SBX_Right :
+                   offset_x - MIDPOSX);
+
+       scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
+                   offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
+                   offset_y - MIDPOSY);
+      }
     }
     else
     {
@@ -3491,8 +3521,8 @@ void RelocatePlayer(int jx, int jy, int el_player_raw)
   }
 
   /* only visually relocate centered player */
-  DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
-                    level.instant_relocation);
+  DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
+                    FALSE, level.instant_relocation);
 
   TestIfPlayerTouchesBadThing(jx, jy);
   TestIfPlayerTouchesCustomElement(jx, jy);
@@ -9337,7 +9367,7 @@ void GameActions_RND()
     game.centered_player_nr = game.centered_player_nr_next;
     game.set_centered_player = FALSE;
 
-    DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
+    DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
     DrawGameDoorValues();
   }
 
@@ -11056,6 +11086,21 @@ void KillPlayer(struct PlayerInfo *player)
   if (!player->active)
     return;
 
+  /* the following code was introduced to prevent an infinite loop when calling
+     -> Bang()
+     -> CheckTriggeredElementChangeExt()
+     -> ExecuteCustomElementAction()
+     -> KillPlayer()
+     -> (infinitely repeating the above sequence of function calls)
+     which occurs when killing the player while having a CE with the setting
+     "kill player X when explosion of <player X>"; the solution using a new
+     field "player->killed" was chosen for backwards compatibility, although
+     clever use of the fields "player->active" etc. would probably also work */
+  if (player->killed)
+    return;
+
+  player->killed = TRUE;
+
   /* remove accessible field at the player's position */
   Feld[jx][jy] = EL_EMPTY;