added optional button to restart game (door, panel and touch variants)
[rocksndiamonds.git] / src / tape.c
index 0a63b79578c0aee5f24dacbd32c1fe7348d1a4e4..f16e7ec9451a20ab68068add00155ed01870088a 100644 (file)
@@ -18,6 +18,8 @@
 #include "files.h"
 #include "network.h"
 #include "anim.h"
+#include "api.h"
+
 
 #define DEBUG_TAPE_WHEN_PLAYING                        FALSE
 
@@ -662,6 +664,7 @@ void TapeErase(void)
   tape.level_nr = level_nr;
   tape.pos[tape.counter].delay = 0;
   tape.changed = TRUE;
+  tape.solved = FALSE;
 
   tape.random_seed = InitRND(level.random_seed);
 
@@ -758,6 +761,7 @@ static void TapeAppendRecording(void)
   // start recording
   tape.recording = TRUE;
   tape.changed = TRUE;
+  tape.solved = FALSE;
 
   // set current delay (for last played move)
   tape.pos[tape.counter].delay = tape.delay_played;
@@ -775,7 +779,9 @@ static void TapeAppendRecording(void)
 
 void TapeHaltRecording(void)
 {
-  tape.counter++;
+  // only advance tape counter if any input events have been recorded
+  if (tape.pos[tape.counter].delay > 0)
+    tape.counter++;
 
   // initialize delay for next tape entry (to be able to continue recording)
   if (tape.counter < MAX_TAPE_LEN)
@@ -830,6 +836,8 @@ boolean TapeAddAction(byte action[MAX_TAPE_ACTIONS])
     tape.pos[tape.counter].delay++;
   }
 
+  tape.changed = TRUE;
+
   return TRUE;
 }
 
@@ -928,6 +936,10 @@ void TapeTogglePause(boolean toggle_mode)
 
     ModifyPauseButtons();
   }
+
+  // stop tape when leaving auto-pause after completely replaying tape
+  if (tape.playing && !tape.pausing && tape.counter >= tape.length)
+    TapeStop();
 }
 
 void TapeStartPlaying(void)
@@ -985,21 +997,27 @@ byte *TapePlayAction(void)
 
   if (tape.pause_before_end)  // stop some seconds before end of tape
   {
-    if (TapeTime > tape.length_seconds - TAPE_PAUSE_SECONDS_BEFORE_DEATH)
+    if (TapeTime > (int)tape.length_seconds - TAPE_PAUSE_SECONDS_BEFORE_DEATH)
     {
       TapeStopWarpForward();
       TapeTogglePause(TAPE_TOGGLE_MANUAL);
 
+      if (setup.autorecord_after_replay)
+       TapeAppendRecording();
+
       return NULL;
     }
   }
 
   if (tape.counter >= tape.length)     // end of tape reached
   {
-    if (tape.warp_forward && !tape.auto_play)
+    if (!tape.auto_play)
     {
       TapeStopWarpForward();
       TapeTogglePause(TAPE_TOGGLE_MANUAL);
+
+      if (setup.autorecord_after_replay)
+       TapeAppendRecording();
     }
     else
     {
@@ -1088,6 +1106,27 @@ void TapeStop(void)
   }
 }
 
+static void TapeStopGameOrTape(boolean stop_game)
+{
+  if (score_info_tape_play || (!tape.playing && stop_game))
+    RequestQuitGame(FALSE);
+  else
+    TapeStop();
+}
+
+void TapeStopGame(void)
+{
+  if (game_status == GAME_MODE_MAIN)
+    return;
+
+  TapeStopGameOrTape(TRUE);
+}
+
+void TapeStopTape(void)
+{
+  TapeStopGameOrTape(FALSE);
+}
+
 unsigned int GetTapeLengthFrames(void)
 {
   unsigned int tape_length_frames = 0;
@@ -1154,22 +1193,25 @@ static void TapeSingleStep(void)
 
 void TapeQuickSave(void)
 {
-  if (game_status == GAME_MODE_MAIN)
+  if (game_status != GAME_MODE_PLAYING)
   {
-    Request("No game that can be saved!", REQ_CONFIRM);
+    Request("No game that could be saved!", REQ_CONFIRM);
 
     return;
   }
 
-  if (game_status != GAME_MODE_PLAYING)
+  if (!tape.recording)
+  {
+    Request("No recording that could be saved!", REQ_CONFIRM);
+
     return;
+  }
 
-  if (tape.recording)
-    TapeHaltRecording();       // prepare tape for saving on-the-fly
+  TapeHaltRecording();         // prepare tape for saving on-the-fly
 
   if (TAPE_IS_EMPTY(tape))
   {
-    Request("No tape that can be saved!", REQ_CONFIRM);
+    Request("No tape that could be saved!", REQ_CONFIRM);
 
     return;
   }
@@ -1244,6 +1286,69 @@ void TapeQuickLoad(void)
   }
 }
 
+static boolean checkRestartGame(char *message)
+{
+  if (game_status == GAME_MODE_MAIN)
+    return TRUE;
+
+  if (!hasStartedNetworkGame())
+    return FALSE;
+
+  if (level_editor_test_game)
+    return TRUE;
+
+  if (game.all_players_gone)
+    return TRUE;
+
+  if (!setup.ask_on_quit_game)
+    return TRUE;
+
+  if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
+    return TRUE;
+
+  OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
+
+  return FALSE;
+}
+
+void TapeRestartGame(void)
+{
+  if (score_info_tape_play)
+  {
+    TapeStartGamePlaying();
+
+    return;
+  }
+
+  if (!checkRestartGame("Restart game?"))
+    return;
+
+  StartGameActions(network.enabled, setup.autorecord, level.random_seed);
+}
+
+void TapeReplayAndPauseBeforeEnd(void)
+{
+  if (score_info_tape_play)
+    return;
+
+  if (TAPE_IS_EMPTY(tape) && !tape.recording)
+  {
+    Request("No tape for this level!", REQ_CONFIRM);
+
+    return;
+  }
+
+  if (!checkRestartGame("Replay game and pause before end?"))
+    return;
+
+  TapeStop();
+  TapeStartGamePlaying();
+  TapeStartWarpForward(AUTOPLAY_MODE_WARP_NO_DISPLAY);
+
+  tape.pause_before_end = TRUE;
+  tape.quick_resume = TRUE;
+}
+
 boolean hasSolutionTape(void)
 {
   boolean tape_file_exists = fileExists(getSolutionTapeFilename(level_nr));
@@ -1292,6 +1397,93 @@ boolean PlaySolutionTape(void)
   return TRUE;
 }
 
+static boolean PlayScoreTape_WaitForDownload(void)
+{
+  DelayCounter download_delay = { 10000 };
+
+  ResetDelayCounter(&download_delay);
+
+  // wait for score tape to be successfully downloaded (and fail on timeout)
+  while (!server_scores.tape_downloaded)
+  {
+    if (DelayReached(&download_delay))
+      return FALSE;
+
+    UPDATE_BUSY_STATE_NOT_LOADING();
+
+    Delay(20);
+  }
+
+  return TRUE;
+}
+
+boolean PlayScoreTape(int entry_nr)
+{
+  struct ScoreEntry *entry = &scores.entry[entry_nr];
+  char *tape_filename =
+    (entry->id == -1 ?
+     getScoreTapeFilename(entry->tape_basename, level_nr) :
+     getScoreCacheTapeFilename(entry->tape_basename, level_nr));
+  boolean download_tape = (!fileExists(tape_filename));
+
+  if (download_tape && entry->id == -1)
+  {
+    FadeSkipNextFadeIn();
+
+    Request("Cannot find score tape!", REQ_CONFIRM);
+
+    return FALSE;
+  }
+
+  server_scores.tape_downloaded = FALSE;
+
+  if (download_tape)
+    ApiGetScoreTapeAsThread(level_nr, entry->id, entry->tape_basename);
+
+  SetGameStatus(GAME_MODE_PLAYING);
+
+  FadeOut(REDRAW_FIELD);
+
+  if (download_tape && !PlayScoreTape_WaitForDownload())
+  {
+    SetGameStatus(GAME_MODE_SCOREINFO);
+    ClearField();
+
+    Request("Cannot download score tape from score server!", REQ_CONFIRM);
+
+    return FALSE;
+  }
+
+  if (!TAPE_IS_STOPPED(tape))
+    TapeStop();
+
+  // if tape recorder already contains a tape, remove it without asking
+  TapeErase();
+
+  if (entry->id == -1)
+    LoadScoreTape(entry->tape_basename, level_nr);
+  else
+    LoadScoreCacheTape(entry->tape_basename, level_nr);
+
+  if (TAPE_IS_EMPTY(tape))
+  {
+    SetGameStatus(GAME_MODE_SCOREINFO);
+    ClearField();
+
+    Request("Cannot load score tape for this level!", REQ_CONFIRM);
+
+    return FALSE;
+  }
+
+  FadeSkipNextFadeOut();
+
+  TapeStartGamePlaying();
+
+  score_info_tape_play = TRUE;
+
+  return TRUE;
+}
+
 static boolean checkTapesFromSameLevel(struct TapeInfo *t1, struct TapeInfo *t2)
 {
   return (strEqual(t1->level_identifier, t2->level_identifier) &&
@@ -1404,15 +1596,14 @@ static void AutoPlayTapes_SetScoreEntry(int score, int time)
 
 static boolean AutoPlayTapes_WaitForUpload(void)
 {
-  unsigned int upload_delay = 0;
-  unsigned int upload_delay_value = 10000;
+  DelayCounter upload_delay = { 10000 };
 
   ResetDelayCounter(&upload_delay);
 
   // wait for score tape to be successfully uploaded (and fail on timeout)
   while (!server_scores.uploaded)
   {
-    if (DelayReached(&upload_delay, upload_delay_value))
+    if (DelayReached(&upload_delay))
     {
       PrintNoLog("\r");
       Print("- uploading score tape to score server - TIMEOUT.\n");
@@ -2340,7 +2531,7 @@ static void HandleTapeButtonsExt(int id)
       break;
 
     case TAPE_CTRL_ID_STOP:
-      TapeStop();
+      TapeStopTape();
 
       break;