fixed bug where player actions were incorrectly mapped in single player mode (also...
[rocksndiamonds.git] / src / game.c
index f17eb32359f76fccb2c1e25371125322ae9d1b26..cc6bcdfc33e33cedb876ec0bd0cb63d3f821c5b1 100644 (file)
 #define DEBUG_INIT_PLAYER      1
 #define DEBUG_PLAYER_ACTIONS   0
 
-// test element position in level set "test_gfxframe" / level "000"
-#define DEBUG_GFXFRAME_X       11
-#define DEBUG_GFXFRAME_Y       9
-
 /* EXPERIMENTAL STUFF */
 #define USE_NEW_AMOEBA_CODE    FALSE
 
@@ -3044,11 +3040,14 @@ static void InitGameEngine()
   for (i = 0; i < MAX_PLAYERS; i++)
     game.snapshot.last_action[i] = 0;
   game.snapshot.changed_action = FALSE;
+  game.snapshot.collected_item = FALSE;
   game.snapshot.mode =
     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
      SNAPSHOT_MODE_EVERY_STEP :
      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
-     SNAPSHOT_MODE_EVERY_MOVE : SNAPSHOT_MODE_OFF);
+     SNAPSHOT_MODE_EVERY_MOVE :
+     strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
+     SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
 }
 
 int get_num_special_action(int element, int action_first, int action_last)
@@ -3095,11 +3094,6 @@ void InitGame()
   int initial_move_dir = MV_DOWN;
   int i, j, x, y;
 
-#if 1
-  printf("::: game.graphics_engine_version == %d\n",
-        game.graphics_engine_version);
-#endif
-
   // required here to update video display before fading (FIX THIS)
   DrawMaskedBorder(REDRAW_DOOR_2);
 
@@ -3395,10 +3389,6 @@ void InitGame()
     GfxRedraw[x][y] = GFX_REDRAW_NONE;
   }
 
-#if 1
-  printf("::: INIT GAME");
-#endif
-
   SCAN_PLAYFIELD(x, y)
   {
     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
@@ -3413,10 +3403,6 @@ void InitGame()
     ResetGfxAnimation(x, y);
   }
 
-#if 1
-  printf(" -> %d\n", GfxFrame[DEBUG_GFXFRAME_X][DEBUG_GFXFRAME_Y]);
-#endif
-
   InitBeltMovement();
 
   for (i = 0; i < MAX_PLAYERS; i++)
@@ -4445,8 +4431,6 @@ void GameEnd()
 
   if (!local_player->LevelSolved_SaveScore)
   {
-    FadeOut(REDRAW_FIELD);
-
     game_status = GAME_MODE_MAIN;
 
     DrawMainMenu();
@@ -4478,8 +4462,6 @@ void GameEnd()
   }
   else
   {
-    FadeOut(REDRAW_FIELD);
-
     game_status = GAME_MODE_MAIN;
 
     if (raise_level)
@@ -4616,11 +4598,6 @@ static void ResetGfxAnimation(int x, int y)
   GfxFrame[x][y] = 0;
 
   ResetGfxFrame(x, y, FALSE);
-
-#if 1
-  if (x == DEBUG_GFXFRAME_X && y == DEBUG_GFXFRAME_Y)
-    printf(" (RESET_GFX_ANIM)");
-#endif
 }
 
 static void ResetRandomAnimationValue(int x, int y)
@@ -4917,167 +4894,101 @@ void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
   boolean no_delay = (tape.warp_forward);
   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
+  int new_scroll_x, new_scroll_y;
 
   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
   {
-    RedrawPlayfield();
-  }
-  else if (quick_relocation)
-  {
-    if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
-    {
-      if (!level.shifted_relocation || center_screen)
-      {
-       /* quick relocation (without scrolling), with centering of 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);
-
-       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
-                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
-                              old_y - MIDPOSY);
+    /* case 1: quick relocation inside visible screen (without scrolling) */
 
-       int offset_x = x + (scroll_x - center_scroll_x);
-       int offset_y = y + (scroll_y - center_scroll_y);
+    RedrawPlayfield();
 
-       scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
-                   offset_x > SBX_Right + MIDPOSX ? SBX_Right :
-                   offset_x - MIDPOSX);
+    return;
+  }
 
-       scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
-                   offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
-                   offset_y - MIDPOSY);
-      }
-    }
-    else
-    {
-      if (!level.shifted_relocation || center_screen)
-      {
-       /* quick relocation (without scrolling), with centering of screen */
+  if (!level.shifted_relocation || center_screen)
+  {
+    /* relocation _with_ centering of screen */
 
-       scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
+    new_scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
                    x > SBX_Right + MIDPOSX ? SBX_Right :
                    x - MIDPOSX);
 
-       scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
+    new_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 */
+  }
+  else
+  {
+    /* relocation _without_ centering of screen */
 
-       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
-                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
-                              old_x - MIDPOSX);
+    int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
+                          old_x > SBX_Right + MIDPOSX ? SBX_Right :
+                          old_x - MIDPOSX);
 
-       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
-                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
-                              old_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);
+    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 :
+    new_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 :
+    new_scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
                    offset_y - MIDPOSY);
-      }
-    }
-
-    RedrawPlayfield();
   }
-  else
-  {
-    int scroll_xx, scroll_yy;
-
-    if (!level.shifted_relocation || center_screen)
-    {
-      /* visible relocation (with scrolling), with centering of screen */
-
-      scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
-                  x > SBX_Right + MIDPOSX ? SBX_Right :
-                  x - MIDPOSX);
-
-      scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
-                  y > SBY_Lower + MIDPOSY ? SBY_Lower :
-                  y - MIDPOSY);
-    }
-    else
-    {
-      /* visible relocation (with 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);
-
-      int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
-                            old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
-                            old_y - MIDPOSY);
+  if (quick_relocation)
+  {
+    /* case 2: quick relocation (redraw without visible scrolling) */
 
-      int offset_x = x + (scroll_x - center_scroll_x);
-      int offset_y = y + (scroll_y - center_scroll_y);
+    scroll_x = new_scroll_x;
+    scroll_y = new_scroll_y;
 
-      scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
-                  offset_x > SBX_Right + MIDPOSX ? SBX_Right :
-                  offset_x - MIDPOSX);
+    RedrawPlayfield();
 
-      scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
-                  offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
-                  offset_y - MIDPOSY);
-    }
+    return;
+  }
 
-    ScrollScreen(NULL, SCROLL_GO_ON);  /* scroll last frame to full tile */
+  /* case 3: visible relocation (with scrolling to new position) */
 
-    while (scroll_x != scroll_xx || scroll_y != scroll_yy)
-    {
-      int dx = 0, dy = 0;
-      int fx = FX, fy = FY;
+  ScrollScreen(NULL, SCROLL_GO_ON);    /* scroll last frame to full tile */
 
-      dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
-      dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
+  while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
+  {
+    int dx = 0, dy = 0;
+    int fx = FX, fy = FY;
 
-      if (dx == 0 && dy == 0)          /* no scrolling needed at all */
-       break;
+    dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
+    dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
 
-      scroll_x -= dx;
-      scroll_y -= dy;
+    if (dx == 0 && dy == 0)            /* no scrolling needed at all */
+      break;
 
-      fx += dx * TILEX / 2;
-      fy += dy * TILEY / 2;
+    scroll_x -= dx;
+    scroll_y -= dy;
 
-      ScrollLevel(dx, dy);
-      DrawAllPlayers();
+    fx += dx * TILEX / 2;
+    fy += dy * TILEY / 2;
 
-      /* scroll in two steps of half tile size to make things smoother */
-      BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
-      Delay(wait_delay_value);
+    ScrollLevel(dx, dy);
+    DrawAllPlayers();
 
-      /* scroll second step to align at full tile size */
-      BackToFront();
-      Delay(wait_delay_value);
-    }
+    /* scroll in two steps of half tile size to make things smoother */
+    BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
+    Delay(wait_delay_value);
 
-    DrawAllPlayers();
+    /* scroll second step to align at full tile size */
     BackToFront();
     Delay(wait_delay_value);
   }
+
+  DrawAllPlayers();
+  BackToFront();
+  Delay(wait_delay_value);
 }
 
 void RelocatePlayer(int jx, int jy, int el_player_raw)
@@ -9586,6 +9497,8 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
     {
       local_player->gems_still_needed = action_arg_number_new;
 
+      game.snapshot.collected_item = TRUE;
+
       game_panel_controls[GAME_PANEL_GEMS].value =
        local_player->gems_still_needed;
 
@@ -10018,11 +9931,6 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change)
     ResetGfxAnimation(x, y);
     ResetRandomAnimationValue(x, y);
 
-#if 0
-  if (x == DEBUG_GFXFRAME_X && y == DEBUG_GFXFRAME_Y)
-    printf(" (RESET X)");
-#endif
-
     TEST_DrawLevelField(x, y);
 
     if (GFX_CRUMBLED(new_element))
@@ -10293,10 +10201,9 @@ static void HandleElementChange(int x, int y, int page)
        be drawn instead of the correct frames 0,1,2,3. This is caused by
        "GfxFrame[][]" being reset *twice* (in two successive frames) after
        an element change: First when the change delay ("ChangeDelay[][]")
-       counter has reached zero after decrementing (see "RESET 1" below),
-       then a second time in the next frame (after "GfxFrame[][]" was
-       already incremented) when "ChangeDelay[][]" is reset to the initial
-       delay value again (see "RESET 2" below).
+       counter has reached zero after decrementing, then a second time in
+       the next frame (after "GfxFrame[][]" was already incremented) when
+       "ChangeDelay[][]" is reset to the initial delay value again.
 
        This causes frame 0 to be drawn twice, while the last frame won't
        be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
@@ -10327,12 +10234,6 @@ static void HandleElementChange(int x, int y, int page)
       {
        ResetGfxAnimation(x, y);
        ResetRandomAnimationValue(x, y);
-
-#if 1
-       if (x == DEBUG_GFXFRAME_X && y == DEBUG_GFXFRAME_Y)
-         printf(" (RESET 2)");
-#endif
-
       }
 
       if (change->pre_change_function)
@@ -10387,12 +10288,6 @@ static void HandleElementChange(int x, int y, int page)
        if (change->post_change_function)
          change->post_change_function(x, y);
       }
-
-#if 1
-      if (x == DEBUG_GFXFRAME_X && y == DEBUG_GFXFRAME_Y)
-       printf(" (RESET 1)");
-#endif
-
     }
 
     if (change->has_action && !handle_action_before_change)
@@ -11202,8 +11097,11 @@ void GameActions()
     SendToServer_MovePlayer(summarized_player_action);
 #endif
 
+  // summarize all actions at local players mapped input device position
+  // (this allows using different input devices in single player mode)
   if (!options.network && !game.team_mode)
-    local_player->effective_action = summarized_player_action;
+    stored_player[map_player_action[local_player->index_nr]].effective_action =
+      summarized_player_action;
 
   if (tape.recording &&
       setup.team_mode &&
@@ -11239,6 +11137,7 @@ void GameActions()
 #if USE_NEW_PLAYER_ASSIGNMENTS
   // !!! also map player actions in single player mode !!!
   // if (game.team_mode)
+  if (1)
   {
     byte mapped_action[MAX_PLAYERS];
 
@@ -11469,10 +11368,6 @@ void GameActions_RND()
     }
   }
 
-#if 1
-  printf("::: %d", GfxFrame[DEBUG_GFXFRAME_X][DEBUG_GFXFRAME_Y]);
-#endif
-
   SCAN_PLAYFIELD(x, y)
   {
     ChangeCount[x][y] = 0;
@@ -11545,10 +11440,6 @@ void GameActions_RND()
 #endif
   }
 
-#if 1
-  printf(" -> %d", GfxFrame[DEBUG_GFXFRAME_X][DEBUG_GFXFRAME_Y]);
-#endif
-
   SCAN_PLAYFIELD(x, y)
   {
     element = Feld[x][y];
@@ -11691,10 +11582,6 @@ void GameActions_RND()
     }
   }
 
-#if 1
-  printf(" -> %d\n", GfxFrame[DEBUG_GFXFRAME_X][DEBUG_GFXFRAME_Y]);
-#endif
-
 #if USE_NEW_AMOEBA_CODE
   /* new experimental amoeba growth stuff */
   if (!(FrameCounter % 8))
@@ -13580,6 +13467,8 @@ static int DigField(struct PlayerInfo *player,
       if (local_player->gems_still_needed < 0)
        local_player->gems_still_needed = 0;
 
+      game.snapshot.collected_item = TRUE;
+
       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
 
       DisplayGameControlValues();
@@ -13778,9 +13667,16 @@ static int DigField(struct PlayerInfo *player,
       SCAN_PLAYFIELD(xx, yy)
       {
        if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
+       {
          Bang(xx, yy);
+       }
        else if (Feld[xx][yy] == EL_SP_TERMINAL)
+       {
          Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
+
+         ResetGfxAnimation(xx, yy);
+         TEST_DrawLevelField(xx, yy);
+       }
       }
     }
     else if (IS_BELT_SWITCH(element))
@@ -14572,19 +14468,11 @@ void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
 #endif
     {
       if (quick_quit)
-      {
        FadeSkipNextFadeIn();
 
-       game_status = GAME_MODE_MAIN;
+      game_status = GAME_MODE_MAIN;
 
-       DrawMainMenu();
-      }
-      else
-      {
-       game_status = GAME_MODE_MAIN;
-
-       DrawMainMenu();
-      }
+      DrawMainMenu();
     }
   }
   else         /* continue playing the game */
@@ -14875,9 +14763,12 @@ static boolean SaveEngineSnapshotToListExt(boolean initial_snapshot)
     (initial_snapshot ||
      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
-      game.snapshot.changed_action));
+      game.snapshot.changed_action) ||
+     (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
+      game.snapshot.collected_item));
 
   game.snapshot.changed_action = FALSE;
+  game.snapshot.collected_item = FALSE;
 
   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
       tape.quick_resume ||
@@ -15219,6 +15110,7 @@ void GameRedo(int steps)
 
 static void HandleGameButtonsExt(int id, int button)
 {
+  static boolean game_undo_executed = FALSE;
   int steps = BUTTON_STEPSIZE(button);
   boolean handle_game_buttons =
     (game_status == GAME_MODE_PLAYING ||
@@ -15253,6 +15145,9 @@ static void HandleGameButtonsExt(int id, int button)
       }
       else
        TapeTogglePause(TAPE_TOGGLE_MANUAL);
+
+      game_undo_executed = FALSE;
+
       break;
 
     case GAME_CTRL_ID_PLAY:
@@ -15272,6 +15167,17 @@ static void HandleGameButtonsExt(int id, int button)
       break;
 
     case GAME_CTRL_ID_UNDO:
+      // Important: When using "save snapshot when collecting an item" mode,
+      // load last (current) snapshot for first "undo" after pressing "pause"
+      // (else the last-but-one snapshot would be loaded, because the snapshot
+      // pointer already points to the last snapshot when pressing "pause",
+      // which is fine for "every step/move" mode, but not for "every collect")
+      if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
+         !game_undo_executed)
+       steps--;
+
+      game_undo_executed = TRUE;
+
       GameUndo(steps);
       break;