fixed half-tile shifting when teleporting between playfield borders
[rocksndiamonds.git] / src / game.c
index ea30528996c15d9b1a706d899d0d6f0853dd5bd7..94b74f786cdc2c7d211af21d484a3cec17a22eb0 100644 (file)
@@ -3474,8 +3474,9 @@ static void InitGameEngine(void)
      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
      !setup.forced_scroll_delay           ? 0 :
      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
-  game.scroll_delay_value =
-    MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
+  if (game.forced_scroll_delay_value == -1)
+    game.scroll_delay_value =
+      MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
 
   // ---------- initialize game engine snapshots ------------------------------
   for (i = 0; i < MAX_PLAYERS; i++)
@@ -4444,6 +4445,11 @@ void InitGame(void)
     scroll_y = SCROLL_POSITION_Y(local_player->jy);
   }
 
+  if (game.forced_scroll_x != ARG_UNDEFINED_VALUE)
+    scroll_x = game.forced_scroll_x;
+  if (game.forced_scroll_y != ARG_UNDEFINED_VALUE)
+    scroll_y = game.forced_scroll_y;
+
   // !!! FIX THIS (START) !!!
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
   {
@@ -5674,14 +5680,47 @@ static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
   {
     // relocation _without_ centering of screen
 
-    int center_scroll_x = SCROLL_POSITION_X(old_x);
-    int center_scroll_y = SCROLL_POSITION_Y(old_y);
-    int offset_x = x + (scroll_x - center_scroll_x);
-    int offset_y = y + (scroll_y - center_scroll_y);
+    // apply distance between old and new player position to scroll position
+    int shifted_scroll_x = scroll_x + (x - old_x);
+    int shifted_scroll_y = scroll_y + (y - old_y);
+
+    // make sure that shifted scroll position does not scroll beyond screen
+    new_scroll_x = SCROLL_POSITION_X(shifted_scroll_x + MIDPOSX);
+    new_scroll_y = SCROLL_POSITION_Y(shifted_scroll_y + MIDPOSY);
+
+    // special case for teleporting from one end of the playfield to the other
+    // (this kludge prevents the destination area to be shifted by half a tile
+    // against the source destination for even screen width or screen height;
+    // probably most useful when used with high "game.forced_scroll_delay_value"
+    // in combination with "game.forced_scroll_x" and "game.forced_scroll_y")
+    if (quick_relocation)
+    {
+      if (EVEN(SCR_FIELDX))
+      {
+       // relocate (teleport) between left and right border (half or full)
+       if (scroll_x == SBX_Left && new_scroll_x == SBX_Right - 1)
+         new_scroll_x = SBX_Right;
+       else if (scroll_x == SBX_Left + 1 && new_scroll_x == SBX_Right)
+         new_scroll_x = SBX_Right - 1;
+       else if (scroll_x == SBX_Right && new_scroll_x == SBX_Left + 1)
+         new_scroll_x = SBX_Left;
+       else if (scroll_x == SBX_Right - 1 && new_scroll_x == SBX_Left)
+         new_scroll_x = SBX_Left + 1;
+      }
 
-    // for new screen position, apply previous offset to center position
-    new_scroll_x = SCROLL_POSITION_X(offset_x);
-    new_scroll_y = SCROLL_POSITION_Y(offset_y);
+      if (EVEN(SCR_FIELDY))
+      {
+       // relocate (teleport) between top and bottom border (half or full)
+       if (scroll_y == SBY_Upper && new_scroll_y == SBY_Lower - 1)
+         new_scroll_y = SBY_Lower;
+       else if (scroll_y == SBY_Upper + 1 && new_scroll_y == SBY_Lower)
+         new_scroll_y = SBY_Lower - 1;
+       else if (scroll_y == SBY_Lower && new_scroll_y == SBY_Upper + 1)
+         new_scroll_y = SBY_Upper;
+       else if (scroll_y == SBY_Lower - 1 && new_scroll_y == SBY_Upper)
+         new_scroll_y = SBY_Upper + 1;
+      }
+    }
   }
 
   if (quick_relocation)
@@ -10642,13 +10681,22 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change)
     if (GFX_CRUMBLED(new_element))
       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
 
+    if (old_element == EL_EXPLOSION)
+    {
+      Store[x][y] = Store2[x][y] = 0;
+
+      // check if new element replaces an exploding player, requiring cleanup
+      if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
+       StorePlayer[x][y] = 0;
+    }
+
     // check if element under the player changes from accessible to unaccessible
     // (needed for special case of dropping element which then changes)
     // (must be checked after creating new element for walkable group elements)
     if (IS_PLAYER(x, y) && !player_explosion_protected &&
        IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
     {
-      Bang(x, y);
+      KillPlayer(PLAYERINFO(x, y));
 
       return;
     }