Time Travel Reset Protected Across Restart / Restore

Greetings all. I’ve been playing in Inform 7 and Inform 6 with an idea for a time travel concept. The idea is that certain elements need to persist across “resets.” If the player, for example, causes a paradox, this generates a hard restart, which practically resets the world.

However, the player character will know that they have encountered a time reset. So, that knowledge has to persist. Here is a very minimal start that I have:

Include (-
	Array time_reset -> 1;

	[ SetTimeResetFlag;
		@protect time_reset 1;
	];
-).

To set up the time reset flag:
	(- SetTimeResetFlag(); -).

To time reset the world:
	(- @restart; -).

When play begins:
	set up the time reset flag.

To say flag time reset as happened:
	(- time_reset -> 0 = 1; -).

To decide whether time reset has happened:
	(- (time_reset -> 0 == 1) -).

Every turn when time reset has happened:
	say "Time reset has happened."

The Test Lab is a room.

A widget is in the Test Lab.

Instead of taking the widget:
	say "[flag time reset as happened]TIME RESET";
	time reset the world.

This is obviously just a test implementation, but it works across restarts. A challenge is that this does not work across a save/restore action. I don’t think undo will be a problem because the @restart obviates that concern.

However, I’m not sure how to handle the save/restore.

I don’t know how applicable it will be to your specific problem, but my solution was to make an autosave and jump back to that instead of using @restart directly. This avoids any side effects of @protect on restoring, undoing, and so on.

That’s interesting for a whole lot of reasons! I’ll dig into that code.

In my case, the player character would have to “know” that an autosave took place. This is the equivalent of the timeline being “reset,” but they retain the memory of it happening. I feel like I would still need the @protect in that case, right? In other words, I have to preserve “knowledge” that the save/restore happened.

Actually, maybe I combine them? I could see autosaving at the start of the game. Then, when a time reset happens, rather than @restart I just do a restore of that initial save. But I have the “protected” bit as well?

I’ve just tried your example, though admittedly with a build of Inform from the latest code, not the 10.1 release, and it works as I would expect for restart, save and restore. When you say “it does not work”, can you be more explicit about what you expect? What I see is that if I save before the reset is triggered, then trigger the reset, then restore, the reset is still triggered. Which is what I would expect.

Interesting. Let me make sure I’m not confusing myself. So I do these commands:

WAIT
WAIT
TAKE WIDGET
WAIT

After taking the widget (which triggers a restart) I see the message “Time reset has happened.” Now consider this:

WAIT
WAIT
TAKE WIDGET
WAIT
SAVE
WAIT

All fine, of course. So I close down the game. I come back later. Now I do this:

RESTORE
WAIT

I do not see the “Time reset has happened.” So the fact the time travel has happened (and thus the protected state was presumably written into the save file) seems to be lost.

That’s what I would expect. The @protect opcode sets up a protected region of memory, and as the Glulx specification says, the protected “memory is silently unaffected by the state-restoring operations”.

So in the example you quote, the time_reset flag is set in the save file, but the act of restoring the save file does not update the time_reset flag in the interpreter’s memory, because that’s what the protected memory region means. In effect the @protect opcode sets up a region of memory that is unaffected by @restore or @restart. Does that make sense?

If what you want is the flag to be updated when the saved file is restored, you’d need to turn the protect region off before restoring, and turn it back on just after saving (since after a restore that’s where the code executes from).

The trick is, when you restore an autosave, the autosave code knows that it happened. So you silently make the autosave at the start of the game, check if you’re returning from that autosave, and if so, tell the player what happened. Then when you want to reset time, restore that autosave.

AH! Got it. The turn it off part is what I wasn’t thinking about. Much thanks for the clarification.

Nifty! I need to explore both of these options more. Much thanks for this clarification.

My original use case was something like…

Instead of switching on the strange device:
    autosave the game;
    if we restored from an autosave:
        say "…and suddenly you're standing back in front of the machine, your hand on the lever. What happened?";
        stop the action;
    otherwise:
        say "The machine whirs to life, and suddenly the time travel puzzle is going!";
        [whatever other setup here]

To cause a temporal paradox:
    say "Oh no. Time unravels…";
    autorestore the game;
    say "And even the magic of save and restore couldn't save you. Goodbye.";
    end the story.

Possibly you want to have a separate flag, not in the @protect region, which indicates whether the player has seen a reset in their play history. After a time-restart, you look at the protected flag and use that to set the unprotected flag.

I guess really there are two RESTORE cases:

  • Start the game, SAVE, go through a time reset, RESTORE that early saved game (which is from before the reset happened).
  • Start the game, immediately RESTORE a saved game which is from late in the game, after a time reset.

You’ll have to decide what the player is supposed to remember in those two cases. :)