// values for tape handling
#define TAPE_PAUSE_SECONDS_BEFORE_DEATH 5
+#define TAPE_MIN_SECONDS_FOR_UNDO_BUFFER 20
// forward declaration for internal use
static void HandleTapeButtons(struct GadgetInfo *);
static void TapeStopWarpForward(void);
static float GetTapeLengthSecondsFloat(void);
+static void CopyTapeToUndoBuffer(void);
static struct GadgetInfo *tape_gadget[NUM_TAPE_BUTTONS];
+static struct TapeInfo tape_undo_buffer;
// ============================================================================
{
int i;
+ CopyTapeToUndoBuffer();
+
tape.counter = 0;
tape.length = 0;
tape.length_frames = 0;
action[i] = tape.pos[tape.counter].action[i];
#if DEBUG_TAPE_WHEN_PLAYING
- printf("%05d", FrameCounter);
+ DebugContinued("", "%05d", FrameCounter);
for (i = 0; i < MAX_TAPE_ACTIONS; i++)
- printf(" %08x", action[i]);
- printf("\n");
+ DebugContinued("", " %08x", action[i]);
+ DebugContinued("tape:play", "\n");
#endif
tape.set_centered_player = FALSE;
return TRUE;
}
+static boolean checkTapesFromSameLevel(struct TapeInfo *t1, struct TapeInfo *t2)
+{
+ return (strEqual(t1->level_identifier, t2->level_identifier) &&
+ t1->level_nr == t2->level_nr);
+}
+
+static void CopyTape(struct TapeInfo *tape_from, struct TapeInfo *tape_to)
+{
+ if (tape_to->level_identifier != NULL)
+ checked_free(tape_to->level_identifier);
+
+ *tape_to = *tape_from;
+
+ tape_to->level_identifier = getStringCopy(tape_from->level_identifier);
+}
+
+static void SwapTapes(struct TapeInfo *t1, struct TapeInfo *t2)
+{
+ struct TapeInfo tmp = *t1;
+
+ *t1 = *t2;
+ *t2 = tmp;
+}
+
+static void CopyTapeToUndoBuffer(void)
+{
+ // copy tapes to undo buffer if large enough (or larger than last undo tape)
+ // or if the last undo tape is from a different level set or level number
+ if (tape.length_seconds >= TAPE_MIN_SECONDS_FOR_UNDO_BUFFER ||
+ tape.length_seconds >= tape_undo_buffer.length_seconds ||
+ !checkTapesFromSameLevel(&tape, &tape_undo_buffer))
+ {
+ CopyTape(&tape, &tape_undo_buffer);
+ }
+}
+
+void UndoTape(void)
+{
+ // only undo tapes from same level set and with same level number
+ if (!checkTapesFromSameLevel(&tape, &tape_undo_buffer))
+ return;
+
+ if (!TAPE_IS_STOPPED(tape))
+ TapeStop();
+
+ // swap last recorded tape with undo buffer, so undo can be reversed
+ SwapTapes(&tape, &tape_undo_buffer);
+
+ DrawCompleteVideoDisplay();
+}
+
+void FixTape_ForceSinglePlayer(void)
+{
+ int i;
+
+ /* fix single-player tapes that contain player input for more than one
+ player (due to a bug in 3.3.1.2 and earlier versions), which results
+ in playing levels with more than one player in multi-player mode,
+ even though the tape was originally recorded in single-player mode */
+
+ // remove player input actions for all players but the first one
+ for (i = 1; i < MAX_PLAYERS; i++)
+ tape.player_participates[i] = FALSE;
+
+ tape.changed = TRUE;
+}
+
// ----------------------------------------------------------------------------
// tape autoplay functions
global.autoplay_leveldir);
if (autoplay_leveldir == NULL)
- Error(ERR_EXIT, "no such level identifier: '%s'",
- global.autoplay_leveldir);
+ Fail("no such level identifier: '%s'", global.autoplay_leveldir);
leveldir_current = autoplay_leveldir;
if (!global.autoplay_all && !global.autoplay_level[level_nr])
continue;
- Print("Tape %03d: ", level_nr);
-
TapeErase();
LoadLevel(level_nr);
if (level.no_level_file || level.no_valid_file)
{
- Print("(no level found)\n");
+ Print("Tape %03d: (no level found)\n", level_nr);
continue;
}
#if 0
// ACTIVATE THIS FOR LOADING/TESTING OF LEVELS ONLY
- Print("(only testing level)\n");
+ Print("Tape %03d: (only testing level)\n", level_nr);
continue;
#endif
{
num_tape_missing++;
- Print("(no tape found)\n");
+ Print("Tape %03d: (no tape found)\n", level_nr);
continue;
}
if (tape.engine_version < patch_version_first[patch_nr] ||
tape.engine_version > patch_version_last[patch_nr])
{
- PrintNoLog("\r");
- PrintNoLog("Tape %03d %s[%02d:%02d]: (%s %d.%d.%d.%d) - skipped.\n",
- level_nr, tape_patch_info,
- tape.length_seconds / 60, tape.length_seconds % 60,
- "not suitable for version",
- (tape.engine_version / 1000000) % 100,
- (tape.engine_version / 10000 ) % 100,
- (tape.engine_version / 100 ) % 100,
- (tape.engine_version ) % 100);
+ Print("Tape %03d %s[%02d:%02d]: (%s %d.%d.%d.%d) - skipped.\n",
+ level_nr, tape_patch_info,
+ tape.length_seconds / 60, tape.length_seconds % 60,
+ "not suitable for version",
+ (tape.engine_version / 1000000) % 100,
+ (tape.engine_version / 10000 ) % 100,
+ (tape.engine_version / 100 ) % 100,
+ (tape.engine_version ) % 100);
if (patch_name[patch_nr + 1] != NULL)
{
global.patchtapes_leveldir);
if (patchtapes_leveldir == NULL)
- Error(ERR_EXIT, "no such level identifier: '%s'",
- global.patchtapes_leveldir);
+ Fail("no such level identifier: '%s'", global.patchtapes_leveldir);
leveldir_current = patchtapes_leveldir;
GDI_END);
if (gi == NULL)
- Error(ERR_EXIT, "cannot create gadget");
+ Fail("cannot create gadget");
tape_gadget[id] = gi;
}