added step-based engine snapshots to undo/redo game steps
authorHolger Schemel <info@artsoft.org>
Tue, 24 Mar 2015 13:11:04 +0000 (14:11 +0100)
committerHolger Schemel <info@artsoft.org>
Tue, 24 Mar 2015 13:11:04 +0000 (14:11 +0100)
18 files changed:
graphics/gfx_classic/RocksDoor.ilbm
src/conf_gfx.c
src/engines.h
src/events.c
src/game.c
src/game.h
src/game_em/convert.c
src/game_em/export.h
src/game_em/input.c
src/game_em/synchro_1.c
src/game_sp/export.h
src/game_sp/init.c
src/libgame/misc.c
src/libgame/snapshot.c
src/libgame/snapshot.h
src/libgame/types.h
src/tape.c
src/tools.c

index 2170dd948a399abf21ea6b77c85843d3045d51d7..13231581234e1f06237c7b51f713c489c15599ef 100644 (file)
Binary files a/graphics/gfx_classic/RocksDoor.ilbm and b/graphics/gfx_classic/RocksDoor.ilbm differ
index bf3a49b7d6b280c44bf6182809b407d36a743ab8..e749b222c01f9a8e7ecd54a8efd83c56bab3eaf2 100644 (file)
@@ -4721,6 +4721,23 @@ struct ConfigInfo image_config[] =
   { "game.button.gfx.play.width",              "30"                    },
   { "game.button.gfx.play.height",             "30"                    },
   { "game.button.gfx.play.pressed_xoffset",    "-100"                  },
+
+  { "game.button.gfx.undo",                    "RocksDoor.png"         },
+  { "game.button.gfx.undo.x",                  "405"                   },
+  { "game.button.gfx.undo.y",                  "215"                   },
+  { "game.button.gfx.undo.width",              "30"                    },
+  { "game.button.gfx.undo.height",             "30"                    },
+  { "game.button.gfx.undo.pressed_yoffset",    "30"                    },
+  { "game.button.gfx.redo",                    "RocksDoor.png"         },
+  { "game.button.gfx.redo.x",                  "465"                   },
+  { "game.button.gfx.redo.y",                  "215"                   },
+  { "game.button.gfx.redo.width",              "30"                    },
+  { "game.button.gfx.redo.height",             "30"                    },
+  { "game.button.gfx.redo.pressed_yoffset",    "30"                    },
+
+  { "game.button.gfx.save",                    UNDEFINED_FILENAME      },
+  { "game.button.gfx.load",                    UNDEFINED_FILENAME      },
+
   { "game.button.gfx.sound_music",             "RocksDoor.png"         },
   { "game.button.gfx.sound_music.x",           "305"                   },
   { "game.button.gfx.sound_music.y",           "245"                   },
@@ -4743,9 +4760,6 @@ struct ConfigInfo image_config[] =
   { "game.button.gfx.sound_simple.pressed_xoffset", "-100"             },
   { "game.button.gfx.sound_simple.active_yoffset", "-30"               },
 
-  { "game.button.gfx.save",                    UNDEFINED_FILENAME      },
-  { "game.button.gfx.load",                    UNDEFINED_FILENAME      },
-
   { "tape.button.gfx.eject",                   "RocksDoor.png"         },
   { "tape.button.gfx.eject.x",                 "305"                   },
   { "tape.button.gfx.eject.y",                 "357"                   },
@@ -7348,16 +7362,20 @@ struct ConfigInfo image_config[] =
   { "game.button.pause.y",                     "215"                   },
   { "game.button.play.x",                      "65"                    },
   { "game.button.play.y",                      "215"                   },
+  { "game.button.undo.x",                      "5"                     },
+  { "game.button.undo.y",                      "215"                   },
+  { "game.button.redo.x",                      "65"                    },
+  { "game.button.redo.y",                      "215"                   },
+  { "game.button.save.x",                      "-1"                    },
+  { "game.button.save.y",                      "-1"                    },
+  { "game.button.load.x",                      "-1"                    },
+  { "game.button.load.y",                      "-1"                    },
   { "game.button.sound_music.x",               "5"                     },
   { "game.button.sound_music.y",               "245"                   },
   { "game.button.sound_loops.x",               "35"                    },
   { "game.button.sound_loops.y",               "245"                   },
   { "game.button.sound_simple.x",              "65"                    },
   { "game.button.sound_simple.y",              "245"                   },
-  { "game.button.save.x",                      "-1"                    },
-  { "game.button.save.y",                      "-1"                    },
-  { "game.button.load.x",                      "-1"                    },
-  { "game.button.load.y",                      "-1"                    },
 
   { "tape.button.eject.x",                     "5"                     },
   { "tape.button.eject.y",                     "77"                    },
index d1181edb375496a38b51eda8556cf59830c4a660..0fc445774038d65bcf278f73d28bf998313c3291 100644 (file)
@@ -32,7 +32,8 @@ extern int getGameFrameDelay_EM(int);
 
 extern void PlayLevelSound_EM(int, int, int, int);
 extern void InitGraphicInfo_EM(void);
-extern void CheckSingleStepMode_EM(byte action[], int, boolean, boolean);
+extern void CheckSingleStepMode_EM(byte action[], int,
+                                  boolean, boolean, boolean);
 
 void SetGfxAnimation_EM(struct GraphicInfo_EM *, int, int, int, int);
 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *, int, int, int, int);
index d599dde4ba1ee695dcd450730cb23da75074bdef..42277b02e6c1947d5e0176b11657638aa68a7e49 100644 (file)
@@ -932,12 +932,14 @@ void HandleWindowManagerEvent(Event *event)
 void HandleButton(int mx, int my, int button, int button_nr)
 {
   static int old_mx = 0, old_my = 0;
+  boolean button_hold = FALSE;
 
   if (button < 0)
   {
     mx = old_mx;
     my = old_my;
     button = -button;
+    button_hold = TRUE;
   }
   else
   {
@@ -946,6 +948,7 @@ void HandleButton(int mx, int my, int button, int button_nr)
   }
 
 #if defined(PLATFORM_ANDROID)
+  // !!! for now, do not handle gadgets when playing -- maybe fix this !!!
   if (game_status != GAME_MODE_PLAYING &&
       HandleGadgets(mx, my, button))
   {
@@ -960,6 +963,9 @@ void HandleButton(int mx, int my, int button, int button_nr)
   }
 #endif
 
+  if (button_hold && game_status == GAME_MODE_PLAYING && tape.pausing)
+    return;
+
   /* do not use scroll wheel button events for anything other than gadgets */
   if (IS_WHEEL_BUTTON(button_nr))
     return;
@@ -1595,7 +1601,8 @@ void HandleKey(Key key, int key_status)
 
 void HandleNoEvent()
 {
-  if (button_status && game_status != GAME_MODE_PLAYING)
+  // if (button_status && game_status != GAME_MODE_PLAYING)
+  if (button_status && (game_status != GAME_MODE_PLAYING || tape.pausing))
   {
     HandleButton(0, 0, -button_status, button_status);
   }
index 57c7b87326383a566c1e09974e3d4d1aafa928e8..c0153bdf452c0cdc55ada397285bbd9bd3681481 100644 (file)
@@ -15,6 +15,7 @@
 #include "init.h"
 #include "tools.h"
 #include "screens.h"
+#include "events.h"
 #include "files.h"
 #include "tape.h"
 #include "network.h"
@@ -970,13 +971,15 @@ static struct GamePanelControlInfo game_panel_controls[] =
 #define GAME_CTRL_ID_STOP              0
 #define GAME_CTRL_ID_PAUSE             1
 #define GAME_CTRL_ID_PLAY              2
-#define SOUND_CTRL_ID_MUSIC            3
-#define SOUND_CTRL_ID_LOOPS            4
-#define SOUND_CTRL_ID_SIMPLE           5
-#define GAME_CTRL_ID_SAVE              6
-#define GAME_CTRL_ID_LOAD              7
+#define GAME_CTRL_ID_UNDO              3
+#define GAME_CTRL_ID_REDO              4
+#define GAME_CTRL_ID_SAVE              5
+#define GAME_CTRL_ID_LOAD              6
+#define SOUND_CTRL_ID_MUSIC            7
+#define SOUND_CTRL_ID_LOOPS            8
+#define SOUND_CTRL_ID_SIMPLE           9
 
-#define NUM_GAME_BUTTONS               8
+#define NUM_GAME_BUTTONS               10
 
 
 /* forward declaration for internal use */
@@ -3031,6 +3034,8 @@ static void InitGameEngine()
      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
   game.scroll_delay_value =
     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
+
+  FreeEngineSnapshotList();
 }
 
 int get_num_special_action(int element, int action_first, int action_last)
@@ -3991,6 +3996,8 @@ void InitGame()
   }
 
   game.restart_level = FALSE;
+
+  SaveEngineSnapshotToList();
 }
 
 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
@@ -10668,6 +10675,39 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
   }
 }
 
+static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
+{
+  static boolean player_was_moving = FALSE;
+  static boolean player_was_snapping = FALSE;
+  static boolean player_was_dropping = FALSE;
+
+  if (!tape.recording)
+    return;
+
+  if ((!player->is_moving  && player_was_moving) ||
+      (player->MovPos == 0 && player_was_moving) ||
+      (player->is_snapping && !player_was_snapping) ||
+      (player->is_dropping && !player_was_dropping))
+  {
+    SaveEngineSnapshotToList();
+
+    player_was_moving = FALSE;
+    player_was_snapping = TRUE;
+    player_was_dropping = TRUE;
+  }
+  else
+  {
+    if (player->is_moving)
+      player_was_moving = TRUE;
+
+    if (!player->is_snapping)
+      player_was_snapping = FALSE;
+
+    if (!player->is_dropping)
+      player_was_dropping = FALSE;
+  }
+}
+
 static void CheckSingleStepMode(struct PlayerInfo *player)
 {
   if (tape.single_step && tape.recording && !tape.pausing)
@@ -10680,6 +10720,8 @@ static void CheckSingleStepMode(struct PlayerInfo *player)
       SnapField(player, 0, 0);                 /* stop snapping */
     }
   }
+
+  CheckSaveEngineSnapshot(player);
 }
 
 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
@@ -12308,6 +12350,9 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
     if (tape.single_step && tape.recording && !tape.pausing &&
        !player->programmed_action)
       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+
+    if (!player->programmed_action)
+      CheckSaveEngineSnapshot(player);
   }
 }
 
@@ -14551,22 +14596,26 @@ static void LoadEngineSnapshotValues_RND()
   }
 }
 
-void FreeEngineSnapshot()
+void FreeEngineSnapshotSingle()
 {
-  FreeEngineSnapshotBuffers();
+  FreeSnapshotSingle();
 
   setString(&snapshot_level_identifier, NULL);
   snapshot_level_nr = -1;
 }
 
-void SaveEngineSnapshot()
+void FreeEngineSnapshotList()
 {
+  FreeSnapshotList();
+}
+
+ListNode *SaveEngineSnapshotBuffers()
+{
+  ListNode *buffers = NULL;
+
   /* do not save snapshots from editor */
   if (level_editor_test_game)
-    return;
-
-  /* free previous snapshot buffers, if needed */
-  FreeEngineSnapshotBuffers();
+    return NULL;
 
   /* copy some special values to a structure better suited for the snapshot */
 
@@ -14575,82 +14624,82 @@ void SaveEngineSnapshot()
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
     SaveEngineSnapshotValues_EM();
   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
-    SaveEngineSnapshotValues_SP();
+    SaveEngineSnapshotValues_SP(&buffers);
 
   /* save values stored in special snapshot structure */
 
   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
-    SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
+    SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
-    SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
+    SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
-    SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
+    SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
 
   /* save further RND engine values */
 
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
+
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
+
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
+
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
+
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
+
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
+
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
+
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
+
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
+
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
+
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
+
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
+
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
 
   /* save level identification information */
 
@@ -14670,14 +14719,28 @@ void SaveEngineSnapshot()
 
   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
 #endif
+
+  return buffers;
+}
+
+void SaveEngineSnapshotSingle()
+{
+  ListNode *buffers = SaveEngineSnapshotBuffers();
+
+  /* finally save all snapshot buffers to single snapshot */
+  SaveSnapshotSingle(buffers);
 }
 
-void LoadEngineSnapshot()
+void SaveEngineSnapshotToList()
 {
-  /* restore generically stored snapshot buffers */
+  ListNode *buffers = SaveEngineSnapshotBuffers();
 
-  LoadEngineSnapshotBuffers();
+  /* finally save all snapshot buffers to snapshot list */
+  SaveSnapshotToList(buffers);
+}
 
+void LoadEngineSnapshotValues()
+{
   /* restore special values from snapshot structure */
 
   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
@@ -14688,6 +14751,27 @@ void LoadEngineSnapshot()
     LoadEngineSnapshotValues_SP();
 }
 
+void LoadEngineSnapshotSingle()
+{
+  LoadSnapshotSingle();
+
+  LoadEngineSnapshotValues();
+}
+
+void LoadEngineSnapshot_Undo()
+{
+  LoadSnapshotFromList_Older();
+
+  LoadEngineSnapshotValues();
+}
+
+void LoadEngineSnapshot_Redo()
+{
+  LoadSnapshotFromList_Newer();
+
+  LoadEngineSnapshotValues();
+}
+
 boolean CheckEngineSnapshot()
 {
   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
@@ -14718,16 +14802,12 @@ static struct
     GAME_CTRL_ID_PLAY,                 "play game"
   },
   {
-    IMG_GAME_BUTTON_GFX_SOUND_MUSIC,   &game.button.sound_music,
-    SOUND_CTRL_ID_MUSIC,               "background music on/off"
+    IMG_GAME_BUTTON_GFX_UNDO,          &game.button.undo,
+    GAME_CTRL_ID_UNDO,                 "undo step"
   },
   {
-    IMG_GAME_BUTTON_GFX_SOUND_LOOPS,   &game.button.sound_loops,
-    SOUND_CTRL_ID_LOOPS,               "sound loops on/off"
-  },
-  {
-    IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,  &game.button.sound_simple,
-    SOUND_CTRL_ID_SIMPLE,              "normal sounds on/off"
+    IMG_GAME_BUTTON_GFX_REDO,          &game.button.redo,
+    GAME_CTRL_ID_REDO,                 "redo step"
   },
   {
     IMG_GAME_BUTTON_GFX_SAVE,          &game.button.save,
@@ -14736,6 +14816,18 @@ static struct
   {
     IMG_GAME_BUTTON_GFX_LOAD,          &game.button.load,
     GAME_CTRL_ID_LOAD,                 "load game"
+  },
+  {
+    IMG_GAME_BUTTON_GFX_SOUND_MUSIC,   &game.button.sound_music,
+    SOUND_CTRL_ID_MUSIC,               "background music on/off"
+  },
+  {
+    IMG_GAME_BUTTON_GFX_SOUND_LOOPS,   &game.button.sound_loops,
+    SOUND_CTRL_ID_LOOPS,               "sound loops on/off"
+  },
+  {
+    IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,  &game.button.sound_simple,
+    SOUND_CTRL_ID_SIMPLE,              "normal sounds on/off"
   }
 };
 
@@ -14780,6 +14872,13 @@ void CreateGameButtons()
       checked = FALSE;
       event_mask = GD_EVENT_RELEASED;
     }
+    else if (id == GAME_CTRL_ID_UNDO ||
+            id == GAME_CTRL_ID_REDO)
+    {
+      button_type = GD_TYPE_NORMAL_BUTTON;
+      checked = FALSE;
+      event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
+    }
     else
     {
       button_type = GD_TYPE_CHECK_BUTTON;
@@ -14823,12 +14922,32 @@ void FreeGameButtons()
     FreeGadget(game_gadget[i]);
 }
 
+void MapStopPlayButtons()
+{
+  UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
+  UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
+
+  MapGadget(game_gadget[GAME_CTRL_ID_STOP]);
+  MapGadget(game_gadget[GAME_CTRL_ID_PLAY]);
+}
+
+void MapUndoRedoButtons()
+{
+  UnmapGadget(game_gadget[GAME_CTRL_ID_STOP]);
+  UnmapGadget(game_gadget[GAME_CTRL_ID_PLAY]);
+
+  MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
+  MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
+}
+
 void MapGameButtons()
 {
   int i;
 
   for (i = 0; i < NUM_GAME_BUTTONS; i++)
-    MapGadget(game_gadget[i]);
+    if (i != GAME_CTRL_ID_UNDO &&
+       i != GAME_CTRL_ID_REDO)
+      MapGadget(game_gadget[i]);
 }
 
 void UnmapGameButtons()
@@ -14850,6 +14969,41 @@ void RedrawGameButtons()
   redraw_mask &= ~REDRAW_ALL;
 }
 
+void GameUndoRedoExt()
+{
+  ClearPlayerAction();
+
+  tape.pausing = TRUE;
+
+  RedrawPlayfield();
+  UpdateAndDisplayGameControlValues();
+
+  DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
+  DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
+
+  BackToFront();
+}
+
+void GameUndo()
+{
+  if (!CheckEngineSnapshot())
+    return;
+
+  LoadEngineSnapshot_Undo();
+
+  GameUndoRedoExt();
+}
+
+void GameRedo()
+{
+  if (!CheckEngineSnapshot())
+    return;
+
+  LoadEngineSnapshot_Redo();
+
+  GameUndoRedoExt();
+}
+
 static void HandleGameButtonsExt(int id)
 {
   boolean handle_game_buttons =
@@ -14905,6 +15059,22 @@ static void HandleGameButtonsExt(int id)
       }
       break;
 
+    case GAME_CTRL_ID_UNDO:
+      GameUndo();
+      break;
+
+    case GAME_CTRL_ID_REDO:
+      GameRedo();
+      break;
+
+    case GAME_CTRL_ID_SAVE:
+      TapeQuickSave();
+      break;
+
+    case GAME_CTRL_ID_LOAD:
+      TapeQuickLoad();
+      break;
+
     case SOUND_CTRL_ID_MUSIC:
       if (setup.sound_music)
       { 
@@ -14944,14 +15114,6 @@ static void HandleGameButtonsExt(int id)
       }
       break;
 
-    case GAME_CTRL_ID_SAVE:
-      TapeQuickSave();
-      break;
-
-    case GAME_CTRL_ID_LOAD:
-      TapeQuickLoad();
-      break;
-
     default:
       break;
   }
index 86c26eeb9c90b2758ed9b3f10d587ba6aaab380f..3f04c02edfe87c8dba67f54c7b5f173101814469 100644 (file)
@@ -90,12 +90,15 @@ struct GameButtonInfo
   struct XY pause;
   struct XY play;
 
-  struct XY sound_music;
-  struct XY sound_loops;
-  struct XY sound_simple;
+  struct XY undo;
+  struct XY redo;
 
   struct XY save;
   struct XY load;
+
+  struct XY sound_music;
+  struct XY sound_loops;
+  struct XY sound_simple;
 };
 
 struct GameInfo
@@ -346,13 +349,17 @@ void RequestQuitGame(boolean);
 unsigned int InitEngineRandom_RND(int);
 unsigned int RND(int);
 
-void FreeEngineSnapshot();
-void LoadEngineSnapshot();
-void SaveEngineSnapshot();
+void FreeEngineSnapshotSingle();
+void FreeEngineSnapshotList();
+void LoadEngineSnapshotSingle();
+void SaveEngineSnapshotSingle();
+void SaveEngineSnapshotToList();
 boolean CheckEngineSnapshot();
 
 void CreateGameButtons();
 void FreeGameButtons();
+void MapStopPlayButtons();
+void MapUndoRedoButtons();
 void MapGameButtons();
 void UnmapGameButtons();
 void RedrawGameButtons();
index 3631114cd70b033b0531d7798fb902d56d3b7769..d240739efaac68ba01fe913b6c2e2c085e2852fd 100644 (file)
@@ -1108,6 +1108,7 @@ void prepare_em_level(void)
   }
 
   game_em.any_player_moving = FALSE;
+  game_em.any_player_snapping = FALSE;
   game_em.last_moving_player = 0;      /* default: first player */
 
   for (i = 0; i < MAX_PLAYERS; i++)
index 61e95b45451e3651b98f5e294c8abffaad05ea51..837a9baf5e336c46e77c3d87bafb8609bb452e13 100644 (file)
@@ -665,6 +665,7 @@ struct GlobalInfo_EM
 struct GameInfo_EM
 {
   boolean any_player_moving;
+  boolean any_player_snapping;
   int last_moving_player;
   int last_player_direction[MAX_PLAYERS];
 };
index 7b3ae9214f65e38dc40f97ed3d925bd32a0d8ddd..73d1c858362281896c2c42e496e791112f62cc18 100644 (file)
@@ -73,7 +73,7 @@ void UpdateGameDoorValues_EM()
 void GameActions_EM(byte action[MAX_PLAYERS], boolean warp_mode)
 {
   int i;
-  boolean player_is_dropping = FALSE;
+  boolean any_player_dropping = FALSE;
 
   RandomEM = RandomEM * 129 + 1;
 
@@ -103,10 +103,10 @@ void GameActions_EM(byte action[MAX_PLAYERS], boolean warp_mode)
        ply[i].dynamite &&
        ply[i].dynamite_cnt > 0 &&
        ply[i].dynamite_cnt < 5)
-      player_is_dropping = TRUE;
+      any_player_dropping = TRUE;
 
   CheckSingleStepMode_EM(action, frame, game_em.any_player_moving,
-                        player_is_dropping);
+                        game_em.any_player_snapping, any_player_dropping);
 
   game_animscreen();
 
index f09fc84819a0657ad872adc5bc533bbf926cc3f7..7b2e9c807a8387a9554692ed8a91f96d6be8a3b6 100644 (file)
@@ -23,6 +23,7 @@ void synchro_1(void)
   int i;
 
   game_em.any_player_moving = FALSE;
+  game_em.any_player_snapping = FALSE;
 
   /* must test for death and actually kill separately */
   for (i = 0; i < MAX_PLAYERS; i++)
@@ -460,7 +461,7 @@ static void check_player(struct PLAYER *ply)
   }
   else                                 /* player wants to snap */
   {
-    player_digfield(ply, dx, dy);
+    game_em.any_player_snapping = player_digfield(ply, dx, dy);
   }
 }
 
index 3da95312c6addd3e4ac1f81e07211a89a3c43abf..001fb6d7e6145126861bbd92b0877be36e5720b0 100644 (file)
@@ -192,7 +192,7 @@ extern void BlitScreenToBitmap_SP(Bitmap *);
 extern void RedrawPlayfield_SP(boolean);
 
 extern void LoadEngineSnapshotValues_SP();
-extern void SaveEngineSnapshotValues_SP();
+extern void SaveEngineSnapshotValues_SP(ListNode **);
 
 extern int map_key_RND_to_SP(int);
 extern int map_key_SP_to_RND(int);
index 1fef7fae50e82385d624ecdde1abd352d15ea9a3..b515020cd669d91c9ca18db87d6fb536f6a67d83 100644 (file)
@@ -102,7 +102,7 @@ unsigned int InitEngineRandom_SP(int seed)
 /* Supaplex game engine snapshot handling functions                          */
 /* ------------------------------------------------------------------------- */
 
-void SaveEngineSnapshotValues_SP()
+void SaveEngineSnapshotValues_SP(ListNode **buffers)
 {
   int i;
 
@@ -126,66 +126,66 @@ void SaveEngineSnapshotValues_SP()
 
   /* store special data into engine snapshot buffers */
 
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FieldWidth));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FieldHeight));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FieldMax));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(LevelMax));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimerVar));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RandomSeed));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TerminalMaxCycles));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(mScrollX));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(mScrollY));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(mScrollX_last));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(mScrollY_last));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenScrollXPos));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenScrollYPos));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(DisplayMinX));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(DisplayMinY));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(DisplayMaxX));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(DisplayMaxY));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(InfotronsNeeded));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(KillMurphyFlag));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MurphyMoveCounter));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MurphyExplodePos));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(SplitMoveFlag));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RedDiskReleaseMurphyPos));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MurphyPosIndex));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MurphyXPos));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MurphyYPos));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MurphyScreenXPos));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MurphyScreenYPos));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MurphyVarFaceLeft));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RedDiskCount));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RedDiskReleaseFlag));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovingPictureSequencePhase));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RedDiskReleasePhase));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScratchGravity));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GravityFlag));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(SnikSnaksElectronsFrozen));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(YellowDisksExploded));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(YawnSleepCounter));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(LeadOutCounter));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElementLast));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxGraphicLast));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxGraphic));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollMinX));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollMinY));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollMaxX));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollMaxY));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollX));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollY));
-
-  SaveEngineSnapshotBuffer(&PlayField16[-game_sp.preceding_buffer_size],
-                          game_sp.preceding_buffer_size * sizeof(int));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(FieldWidth));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(FieldHeight));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(FieldMax));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(LevelMax));
+
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(TimerVar));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(RandomSeed));
+
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(TerminalMaxCycles));
+
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(mScrollX));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(mScrollY));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(mScrollX_last));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(mScrollY_last));
+
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenScrollXPos));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenScrollYPos));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(DisplayMinX));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(DisplayMinY));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(DisplayMaxX));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(DisplayMaxY));
+
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(InfotronsNeeded));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(KillMurphyFlag));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(MurphyMoveCounter));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(MurphyExplodePos));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(SplitMoveFlag));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(RedDiskReleaseMurphyPos));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(MurphyPosIndex));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(MurphyXPos));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(MurphyYPos));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(MurphyScreenXPos));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(MurphyScreenYPos));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(MurphyVarFaceLeft));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(RedDiskCount));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(RedDiskReleaseFlag));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(MovingPictureSequencePhase));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(RedDiskReleasePhase));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(ScratchGravity));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(GravityFlag));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(SnikSnaksElectronsFrozen));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(YellowDisksExploded));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(YawnSleepCounter));
+
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(LeadOutCounter));
+
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElementLast));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(GfxGraphicLast));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(GfxGraphic));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
+
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollMinX));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollMinY));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollMaxX));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollMaxY));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollX));
+  SaveSnapshotBuffer(buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollY));
+
+  SaveSnapshotBuffer(buffers, &PlayField16[-game_sp.preceding_buffer_size],
+                    game_sp.preceding_buffer_size * sizeof(int));
 }
 
 void LoadEngineSnapshotValues_SP()
index c02d392067f81401ad910a666f72da1eb9e196f8..052509f6f914e8afd9f25985d05f62ef70349a13 100644 (file)
@@ -1965,6 +1965,10 @@ void addNodeToList(ListNode **node_first, char *key, void *content)
   node_new->key = getStringCopy(key);
   node_new->content = content;
   node_new->next = *node_first;
+
+  if (*node_first)
+    (*node_first)->prev = node_new;
+
   *node_first = node_new;
 }
 
@@ -1977,8 +1981,15 @@ void deleteNodeFromList(ListNode **node_first, char *key,
   if (strEqual((*node_first)->key, key))
   {
     checked_free((*node_first)->key);
+
     if (destructor_function)
       destructor_function((*node_first)->content);
+
+    if ((*node_first)->next)
+      (*node_first)->next->prev = (*node_first)->prev;
+
+    checked_free(*node_first);
+
     *node_first = (*node_first)->next;
   }
   else
index 51171d43738d657c6c819f9291e7dcb19939daf6..f57cb5fa6dd757f8de167541aecaaf77006d54be 100644 (file)
 #include "snapshot.h"
 
 
-static ListNode *engine_snapshot_list = NULL;
+static ListNode *snapshot_single = NULL;
+static ListNode *snapshot_list = NULL;
+static ListNode *snapshot_current = NULL;
 
-void SaveEngineSnapshotBuffer(void *buffer, int size)
+static int num_snapshots_in_list = 0;
+
+
+#ifdef DEBUG
+#define DEBUG_SNAPSHOTS                        0
+#endif
+
+
+// -----------------------------------------------------------------------------
+// functions for handling buffers for a single snapshot
+// -----------------------------------------------------------------------------
+
+void SaveSnapshotBuffer(ListNode **snapshot_buffers, void *buffer, int size)
 {
-  struct EngineSnapshotNodeInfo *bi =
-    checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
+  struct SnapshotNodeInfo *bi =
+    checked_calloc(sizeof(struct SnapshotNodeInfo));
 
   bi->buffer_orig = buffer;
   bi->buffer_copy = checked_malloc(size);
@@ -25,29 +39,132 @@ void SaveEngineSnapshotBuffer(void *buffer, int size)
 
   memcpy(bi->buffer_copy, buffer, size);
 
-  addNodeToList(&engine_snapshot_list, NULL, bi);
+  addNodeToList(snapshot_buffers, NULL, bi);
 }
 
-static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
+static void LoadSnapshotBuffer(struct SnapshotNodeInfo *bi)
 {
   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
 }
 
-void LoadEngineSnapshotBuffers()
+void LoadSnapshotBuffers(ListNode *snapshot_buffers)
+{
+  while (snapshot_buffers != NULL)
+  {
+    LoadSnapshotBuffer((struct SnapshotNodeInfo *)snapshot_buffers->content);
+
+    snapshot_buffers = snapshot_buffers->next;
+  }
+}
+
+void FreeSnapshotBuffers(ListNode *snapshot_buffers)
+{
+  while (snapshot_buffers != NULL)
+    deleteNodeFromList(&snapshot_buffers, snapshot_buffers->key, checked_free);
+}
+
+// -----------------------------------------------------------------------------
+// functions for handling one of several snapshots
+// -----------------------------------------------------------------------------
+
+static void FreeSnapshotExt(void *snapshot_buffers_ptr)
+{
+  FreeSnapshotBuffers(snapshot_buffers_ptr);
+}
+
+void FreeSnapshotSingle()
+{
+  FreeSnapshotBuffers(snapshot_single);
+
+  snapshot_single = NULL;
+}
+
+void FreeSnapshotList_UpToNode(ListNode *node)
+{
+  while (snapshot_list != node)
+  {
+    deleteNodeFromList(&snapshot_list, snapshot_list->key, FreeSnapshotExt);
+
+    num_snapshots_in_list--;
+  }
+}
+
+void FreeSnapshotList()
+{
+  FreeSnapshotList_UpToNode(NULL);
+
+  snapshot_current = NULL;
+}
+
+void SaveSnapshotSingle(ListNode *snapshot_buffers)
+{
+  if (snapshot_single)
+    FreeSnapshotSingle();
+
+  snapshot_single = snapshot_buffers;
+}
+
+void SaveSnapshotToList(ListNode *snapshot_buffers)
 {
-  ListNode *node = engine_snapshot_list;
+  if (snapshot_current != snapshot_list)
+    FreeSnapshotList_UpToNode(snapshot_current);
+
+  addNodeToList(&snapshot_list, i_to_a(num_snapshots_in_list),
+               snapshot_buffers);
 
-  while (node != NULL)
+  snapshot_current = snapshot_list;
+
+  num_snapshots_in_list++;
+
+#if DEBUG_SNAPSHOTS
+  printf("::: SaveSnapshotToList() [%s]\n", snapshot_current->key);
+#endif
+}
+
+boolean LoadSnapshotSingle()
+{
+  if (snapshot_single)
   {
-    LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
+    LoadSnapshotBuffers(snapshot_single);
 
-    node = node->next;
+    return TRUE;
   }
+
+  return FALSE;
 }
 
-void FreeEngineSnapshotBuffers()
+boolean LoadSnapshotFromList_Older()
 {
-  while (engine_snapshot_list != NULL)
-    deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
-                      checked_free);
+  if (snapshot_current->next)
+  {
+    snapshot_current = snapshot_current->next;
+
+    LoadSnapshotBuffers(snapshot_current->content);
+
+#if DEBUG_SNAPSHOTS
+    printf("::: LoadSnapshotFromList_Older() [%s]\n", snapshot_current->key);
+#endif
+
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+boolean LoadSnapshotFromList_Newer()
+{
+  if (snapshot_current->prev)
+  {
+    snapshot_current = snapshot_current->prev;
+
+    LoadSnapshotBuffers(snapshot_current->content);
+
+#if DEBUG_SNAPSHOTS
+    printf("::: LoadSnapshotFromList_Newer() [%s]\n", snapshot_current->key);
+#endif
+
+    return TRUE;
+  }
+
+  return FALSE;
 }
index ef63bdf80933f1a5ac396fa22a7102a0fd52b9fc..40b85938f46bd77bd595c3e26f6089b861cb6c8f 100644 (file)
@@ -19,7 +19,7 @@
 /* needed for comfortably saving engine snapshot buffers */
 #define ARGS_ADDRESS_AND_SIZEOF(x)             (&(x)), (sizeof(x))
 
-struct EngineSnapshotNodeInfo
+struct SnapshotNodeInfo
 {
   void *buffer_orig;
   void *buffer_copy;
@@ -27,8 +27,16 @@ struct EngineSnapshotNodeInfo
 };
 
 
-void SaveEngineSnapshotBuffer(void *buffer, int size);
-void LoadEngineSnapshotBuffers();
-void FreeEngineSnapshotBuffers();
+void SaveSnapshotBuffer(ListNode **, void *, int);
+void LoadSnapshotBuffers(ListNode *);
+void FreeSnapshotBuffers(ListNode *);
+
+void SaveSnapshotSingle();
+void SaveSnapshotToList();
+boolean LoadSnapshotSingle();
+boolean LoadSnapshotFromList_Older();
+boolean LoadSnapshotFromList_Newer();
+void FreeSnapshotSingle();
+void FreeSnapshotList();
 
 #endif /* SNAPSHOT_H */
index 0dfed15a1ba29280b6cac09091287218d5825926..acc2c236f35a3f06ba434e90c973015bb9bcaa63 100644 (file)
@@ -72,6 +72,7 @@ struct ListNode
 {
   char *key;
   void *content;
+  struct ListNode *prev;
   struct ListNode *next;
 };
 typedef struct ListNode ListNode;
index f6f75061d9464e978d05506cdee064b4bf09fb4a..a14690d3b23daaf4c7bf1c5150699ff945ef66c3 100644 (file)
@@ -740,9 +740,14 @@ void TapeTogglePause(boolean toggle_manual)
       TapeAppendRecording();
 
       if (!CheckEngineSnapshot())
-       SaveEngineSnapshot();
+       SaveEngineSnapshotSingle();
     }
   }
+
+  if (tape.pausing)
+    MapUndoRedoButtons();
+  else
+    MapStopPlayButtons();
 }
 
 void TapeStartPlaying()
@@ -1002,7 +1007,7 @@ void TapeQuickSave()
   }
 
   if (SaveTapeChecked(tape.level_nr))
-    SaveEngineSnapshot();
+    SaveEngineSnapshotSingle();
 }
 
 void TapeQuickLoad()
@@ -1031,7 +1036,7 @@ void TapeQuickLoad()
   {
     TapeStartGamePlaying();
 
-    LoadEngineSnapshot();
+    LoadEngineSnapshotSingle();
 
     DrawCompleteVideoDisplay();
 
index a95b1f795ec8a65a1117732f8ab5add91214f933..e98ac35b9476491b9b6192a282f16c8c447d99e0 100644 (file)
@@ -7794,13 +7794,65 @@ void InitGraphicInfo_EM(void)
 #endif
 }
 
+void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
+                               boolean any_player_moving,
+                               boolean any_player_snapping,
+                               boolean any_player_dropping)
+{
+  static boolean player_was_waiting = TRUE;
+
+  if (!tape.recording)
+    return;
+
+  if (frame == 0 && !any_player_dropping)
+  {
+    if (!player_was_waiting)
+    {
+      SaveEngineSnapshotToList();
+
+      player_was_waiting = TRUE;
+    }
+  }
+  else if (any_player_moving || any_player_snapping || any_player_dropping)
+  {
+    player_was_waiting = FALSE;
+  }
+}
+
+void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
+                               boolean murphy_is_dropping)
+{
+  static boolean player_was_waiting = FALSE;
+
+  if (!tape.recording)
+    return;
+
+  if (murphy_is_waiting)
+  {
+    if (!player_was_waiting)
+    {
+      SaveEngineSnapshotToList();
+
+      player_was_waiting = TRUE;
+    }
+  }
+  else
+  {
+    player_was_waiting = FALSE;
+  }
+}
+
 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
                            boolean any_player_moving,
-                           boolean player_is_dropping)
+                           boolean any_player_snapping,
+                           boolean any_player_dropping)
 {
   if (tape.single_step && tape.recording && !tape.pausing)
-    if (frame == 0 && !player_is_dropping)
+    if (frame == 0 && !any_player_dropping)
       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+
+  CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
+                            any_player_snapping, any_player_dropping);
 }
 
 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
@@ -7809,6 +7861,8 @@ void CheckSingleStepMode_SP(boolean murphy_is_waiting,
   if (tape.single_step && tape.recording && !tape.pausing)
     if (murphy_is_waiting)
       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+
+  CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
 }
 
 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
@@ -8080,7 +8134,7 @@ void ChangeViewportPropertiesIfNeeded()
       // printf("::: new_tilesize_var != TILESIZE_VAR\n");
 
       // changing tile size invalidates scroll values of engine snapshots
-      FreeEngineSnapshot();
+      FreeEngineSnapshotSingle();
 
       // changing tile size requires update of graphic mapping for EM engine
       init_em_graphics = TRUE;