Unexpected UNDO Problem [Closed]

I'd blocked UNDO in a couple of my games because of problems with some of the puzzles involved. This was done by introducing my own UNDO command. Deciding this was unnecessarily restrictive I tried changing the code in the command to the form:

if (some condition) {
  msg ("Sorry, 'undo' is not available here.")
}
else {
  undo
}

This does invoke undo, but the effect is different. In particular, the map display is not updated, as used to be the case before Quest 5.8. The issue seems connected with the conditional, as all is fine if I just use undo by itself in the command???


I'm not entirely sure how the structure works around the undo command. But a few things spring to mind. Are you modifying the script on the built-in undo command, or creating your own? And in the latter case, have you set the isundo flag on the command to true?

After a quick look, I think what you want might be:

SuppressTurnscripts()
if (some condition) {
  msg ("Sorry, 'undo' is not available here.")
}
else {
  undo
  Grid_DrawPlayerInRoom (game.pov.parent)
}

(the core undo command uses if (GetBoolean (game, "gridmap")) { to check if the map is enabled before redrawing it; but I assume that if you're asking that means that there is a map. If it's available in some areas but not others, you can add the check again)


Thanks for the usual rapid response mrangel! Yes, I'm putting in my own undo command at the user script level, where I don't see an isundo flag?

My main concern is understanding why the following two command scripts have different outcomes?

undo
if (true) {
  undo
}

The script of the built-in undo command is:

  <command name="undo">
    <pattern type="string">[undo]</pattern>
    <script>
      undo
      game.suppressturnscripts = true
      if (GetBoolean (game, "gridmap")){
        Grid_DrawPlayerInRoom (game.pov.parent)
      }
    </script>
    <isundo/>
  </command>

The thing with the map is probably because the core undo command runs the undo script-statement and then redraws the map; which yours doesn't.

You might have to use code view to add the <isundo/>. It isn't shown anywhere in the editor, as far as I'm aware, because the developer didn't consider wanting to create your own undo command.

The flag tells the parser not to create a transaction before starting the command. So in practice, when you have undo in a script, it rolls back to the point where the player entered the last command without an isundo flag. So if your undo command doesn't have isundo, it may attempt to undo itself instead of the previous command.


Not sure what I was doing previously so please ignore my last message for the moment as I can't recreate what I thought I'd tested...
...anyway, I'd also misunderstood what was needed to re-implement the undo command so I'll get on with that.
Thanks again mrangel!


Selective undo now implemented in L Too:
http://textadventures.co.uk/games/view/e4hrqb_vpeo4np1a0jel9g/l-too-another-mathemagical-adventure
Anyone interested should feel free to make sure I've done it properly!


If you're playing with undo, it might be worth learning about begin transaction too. It basically creates a checkpoint that undo can roll back to.

It takes a single parameter, which is a command element (but it doesn't seem to do anything with it, so possibly any object would work).

When you use the undo script command, it restores all objects and their attributes to the values they had at the last time begin transaction was called; but doesn't resume any scripts that were running then (and so doesn't restore local variables).

The parser calls begin transaction immediately before running any player-entered command that doesn't have its isundo flag set to true.


Thanks mrangel. Within my games, I use an attribute in_puzzle and just prevent an undo if it is set. I think this avoids me having to worry about the subtleties of the underlying undo operation...though as I type this I realise an undo could be used to reverse back into a puzzle. I'd better check!


First thing that comes to my mind is something like:

undo
while (game.in_puzzle) {
  undo
}

Don't know if that would work or if it's suitable; but might restrict 'undo' by making it so that when you're doing a puzzle, 'undo' will take you all the way back to the beginning rather than step by step.


If you want to stop the user undoing a puzzle entirely, you could use a turn timeout to unset the flag a turn later; prevent the user undoing the final move of the puzzle.

Thanks, for the first game, the single undo seems fine, so I'll not update it to include the looping suggestion. I'll now see how it looks in the second game...


...oh dear! I need to rethink the use of undo. I found problems with its use in my second game (Giantkiller Too) in relation to puzzles and the above fixes aren't enough.

Just to explain the background here: up to Quest 5.8, undo didn't handle the map properly and as all my games use a map, I excluded undo. No one complained about that but with Quest 5.8 available I decided to remove unnecessary restrictions and allowed undo in all but two games that had extended puzzles. I've now looked at removing that restriction as well but had trouble with one of the games...which has also revealed issues with undo in all of my games!

In these games I allow the map and game panes to be brought in and removed dynamically. Undo is not handling these changes properly. There are several issues. For example, note the effect of the following commands issued on entry to L Too (link given above). Also undo doesn't affect the visibility of the map or games panes.

> map
The map is now shown above. Type map again to hide it.

> map
The map has been hidden. Type map again to restore it.

> undo
Undo: map

> undo
Undo: map

> undo
Undo: map
Error running script: The given key was not present in the dictionary.

All of my games are cruelty-free (there is always a way to recover from any difficulty) so I'm inclined to block the use of undo throughout.


The issue with the map is because undo only restores the state of objects and attributes. It doesn't run any javascript, or notify the frontend.

If you have an attribute to track whether the map is shown or hidden (or similar things), you would probably need to make the undo command check those attributes after undoing, and show or hide as appropriate.
For example:

SuppressTurnscripts()
if (some condition) {
  msg ("Sorry, 'undo' is not available here.")
}
else {
  undo
  Grid_DrawPlayerInRoom (game.pov.parent)
  if (GetBoolean (game, "maphidden")) {
    JS.uiHide("#gamePanel")
  }
  else {
    JS.uiShow("#gamePanel")
  }
}

It's likely that any UI Initialisation code which depends on variables that change during play will need to be copied onto the end of the undo command.


It doesn't run any javascript, or notify the frontend.

Ah, I see now that undo relies on there being code to reverse every operation performed, and as that can't be specified for 'external' operations, there will be a way for undo to fail in many games. I've not seen anyone report a problem with undo or complain if it is missing, so on balance, I'll just block it in future, though there is no rush to make this change.

Thanks for the explanation mrangel!


It shouldn't be too much of a problem, really.

The only things that don't revert are things on the frontend - which would mess up in exactly the same way when you load a saved game.
(using undo is pretty much the same as restoring a save)

In the example above, this code:

  if (GetBoolean (game, "maphidden")) {
    JS.uiHide("#gamePanel")
  }
  else {
    JS.uiShow("#gamePanel")
  }

should already be in your UI Initialisation script, so it's just a case of copying it over.

Actually, when the next version of Quest is proposed, I'll suggest the inclusion of a script, game.userinterfacesync or similar, which would be run by the core libraries both after a successful undo, and after the UI Initialisation script. Make this issue more intuitive so that this code only needs to be updated in one place.


using undo is pretty much the same as restoring a save

I did try out your suggestion of using the restore code in the sample game Cloak of Darkness,
http://textadventures.co.uk/games/view/cxbbr4mqakkylkr80ypjhg/cloak-of-darkness-another-version
but all is not well:

> map
The map is now shown above. Type MAP again to hide it.

> undo
Undo: map

> undo
Undo: map
Error running script: The given key was not present in the dictionary.

Feel free to experiment!


Log in to post a reply.

Support

Forums