undo

undo is a built-in function for reverting to the previous turn. All globals and property values and such are set back to their earlier values, with the notable exception of the word array. Most commonly, it is used by the verb routine DoUndo.

How to call

undo returns a true or false value based on its success, so it is called in the following manner:

x = undo ! the x variable gets set to 1 or 0
! or
if not undo
     "(message to be printed if undo doesn't work)"

The conditional “if not undo” itself executes undo. If it works, the game goes back a turn. If it doesn’t, we print some kind of error message. This is how DoUndo does it.

undo Options

The following explores additional undo options an author has at his or her disposal.

Turning UNDO Off

Permanently

If you are writing some kind of nontraditional game or are just a bad person, you can “#set NO_UNDO” in your game so >UNDO is not recognized as a command (nor will it mention UNDO when EndGame() is called).

Temporarily

If there are sections of your game where you don’t want UNDO to work, you can set the global variable UNDO_OFF to a non-zero value, resulting in an “Unable to undo.” response. Set UNDO_OFF back to 0 when the player is in the clear again.

Limiting The Number of Undos

Normally, the Hugo interpreter will allow UNDO for as many turns as memory allows. On some platforms, this can be more than a thousand turns. Although not necessarily recommended, you could limit the number of times one can UNDO for consistency across platforms or to give the player a larger sense of finality. Let’s use PreParse!

global undo_counter

player_character you "you"
{
    before
        {
        actor PreParse
            {
            ! We check to see if the player is UNDOing
                        ! and add to our undo_counter
            if (word[1] = "undo","u") and word[2] = 0
                {
                    undo_counter++
                    return false
                }
            ! The player hasn't UNDO'd. let's reset the undo_counter!
            else
                {
                undo_counter = 0
                return false
                }
            }
        }
}


! In this example, we limit the player to 5 UNDOs
replace DoUndo
{
    if undo_counter > 5
        {
        "Can't undo anymore."
        return false
        }
    if not UNDO_OFF
    {
        if undo
        {
            PrintStatusline
            DescribePlace(location)
        }
        else
            VMessage(&DoUndo)
    }
    else
        VMessage(&DoUndo)
}

One UNDO To Win Them All

What if you have a game where you want UNDO to take a player back to before he made a game unwinnable (no matter how many turns)? We can loop UNDO and the player won’t suspect a thing! Silly player! (Not always possible; see note at bottom.)

global badness
global turns_since_badness

routine main
{
    if badness  {
            turns_since_badness++
            }
       < rest of your main code >
}

room STARTINGLOCATION "Start Location"
{
       long_desc {
                 "That kiddie pool sure looks inviting!"
                 }
       in_to {
             if not waterwings in player
             badness = 1
             return KiddiePool
             }
}

room KiddiePool "The Kiddie Pool"
{
      long_desc {
                if waterwings is not worn
                       "Oh dear, this pool is actually quite deep. You are drowning! Agonizingly slowly!"
                else
                       "Yay! Splashsville!"
                }
}

replace DoUndo
{
    if badness
        "(Undoing back until the game was winnable.)"
    if not UNDO_OFF
    {
        if undo
        {
                        ! no need to alter the value of turns_since_badness since a successful undo
                        ! decreases it anyways
            if turns_since_badness
            {
            Perform(&DoUndo,0,0,0,1)
            return
            }
            PrintStatusline
            DescribePlace(location)
        }
                ! On the slight chance the interpreter runs out of undo memory before it gets
                ! back to a winnable state
        elseif turns_since_badness
                {
                "Dang, we can't UNDO enough to put you in a winnable position. I hope you saved!"
                 return
                }
                else
            VMessage(&DoUndo)
    }
    else
        VMessage(&DoUndo)
}

Unwinnable position? More like unswimmable position, har har! Of course, you could also use similar code to take a player back to the beginning of a conversation tree that you don’t want them dissecting one move at a time or whatever other scenario you could think of.