Z-machine: changing undo message only at game's end?

Undoing death: custom message - #3 by aschultz was very helpful to me for getting something going in Glulx!

However, the opcodes don’t work in z-machine, or not as I see them. So I have this workaround. The I6 code is from @climbingstars years ago. The test case is simply DIE then UNDO. He simply added an “escape mode” flag to the while loop, so it wasn’t unbreakable.

This code works for my purposes, where I have a joke death. But there’s a problem if there are moving parts e.g. you wait, and an enemy moves into your room, which (according to the game rules) kills you. Then if the game restarts without a real UNDO, the enemy is unshakeable, which defeats the point.

chapter dieing

dieing is an action applying to nothing.

understand the command "die" as something new.

understand "die" as dieing.

carry out dieing:
	end the story;
	the rule succeeds;

when play begins:
	choose row with final response rule of immediately undo rule in table of final question options;
	now final response rule entry is the fake death undo rule;

this is the fake death undo rule:
	say "Resurrecting...";
	resume the story;
	now escape mode is true;

Include (-

[ ASK_FINAL_QUESTION_R;
	print "^";
	(+ escape mode +) = false;
	while ((+ escape mode +) == false) {
		CarryOutActivity(DEALING_WITH_FINAL_QUESTION_ACT);
		DivideParagraphPoint();
	}
];

-) instead of "Ask The Final Question Rule" in "OrderOfPlay.i6t".

The escape mode is a truth state that varies.

So this is probably good enough, but there must be a way I6 lets you do better for a more complex game! How would we do this, preferably in I7, but if not, with minimal fiddling in I6?

From what you’re saying, it seems like the trouble that you’re experiencing is that there is NOT a real undo operation being performed in some cases. If that’s the case, then why interfere with the normal mechanism at all? Is there some reason that a real undo has to be avoided for your joke death?

If so, then I would suggest avoiding any tampering with the default mechanism and just making use of a rule substitution that applies only to the joke death scenario. Something like:

Dying is an action applying to nothing. Understand "die" as dying.

Just pretending is initially false.

Carry out dying:
    now just pretending is true;
    end the story.

This is the joke death undo rule:
    say "OK, I was just kidding, anyway.";
    now just pretending is false;
    resume the story.

The joke death undo rule substitutes for the immediately undo rule when just pretending is true.
1 Like

I only want to change the undo message. But for some reason I thought the undo mechanism couldn’t really be touched, maybe because glulx needed to use opcodes.

I suspect there’s a simple way to just change the undo message only on death, and only for the z-machine, but I don’t see quite where to poke the game.

After asking the question, I tried putting in an IF statement in the core

if ( (+ endgame-death +) { print "This is the end of game death."; } else { L__M(##Miscellany, 13); }

In parser.i6t, but the problem was that undo had already reset the variables by then, and I can’t protect variables from undo in I7.

I’ve changed the title to more accurately reflect what I really want. Thanks for your help!

If that’s all you want to do, can you just situationally change the response? From RESPONSES ALL looks like this is the one you want:

immediately undo rule response (E): "[bracket]Previous turn undone.[close bracket]"

Since UNDO undoes lots of stuff, there might be complexity in figuring out what conditions obtain at the moment the response is issued so this might not work but figured I’d flag the possibility!

2 Likes

Ahh, so your goal is to change the message printed after “undo”, but only when it’s coming from the final question at the end of the game?

2 Likes

Yes, that’s the best way to put it! A different message for end-of-game undo.

(Mike’s example in (E) gets squashed by UNDO as well. But it got me looking up some other random stuff.)

I just noticed this was possible:

Erik Temple’s Undo Output control works to change the message in-game. So there is a way to have different in-game and after-death undo messages.

That means we could change the default message in the I6 code to what we want and shift “report undoing when dead is false” to what the default was, because Undo Output Control doesn’t work on the final menu. But this seems like a really awful shell game.

include Undo Output Control by Erik Temple.

test is a room.

the ball is in test.

report undoing an action: say "In-game undo message!";
	the rule succeeds;

There’s nothing like Glulx’s ability to protect memory locations on the Z-machine.

Only two bits (two bits that are declared legal for the running game to modify, that is) survive the memory reset of an undo operation: The first controls whether or not transcripting is on, the second whether or not fixed-width pitch is on. Setting of either bit by the story triggers a change in state in the interpreter, such that it will either ask how to handle transcript output (with a file prompt window, usually) or it will start printing in fixed-width font.

If you’re not using fixed-width font in your game, you can use that bit as the carrier of a signal, at the cost of modifying text output while the bit is set. (If you’re always using fixed width font, then I suppose clearing that bit can be used as a signal, too.)

The theoretically better alternative would be to use an external file to save state and restore from it, as specified in ZMS 1.1. However, most Z-machine interpreters don’t respect a request to save without a file prompt (see Which interpreters comply with ZMS 1.1 regarding prompt parameter of @save/@restore opcodes?), though WinFrotz and the latest version of Bocfel do.

2 Likes

If that works, I would go with it. Doesn’t seem like too bad of a hack.

I’m not sure where the message that Undo Output Control doesn’t replace is coming from. But if you can find that in the I6Ts, you should be able to change it without relying on the extension.

1 Like

This is really nice! Now you mention UNDO doesn’t adjust the transcript status, I realize I’ve taken advantage of that a lot, so of course it should be reserved.

And I’ve verified that, yes, indeed, fixed width font persists after UNDO. That is really neat to know.

One final question: how would we check the font flags? I see how to set them. And I know how to see if we are transcripting:

Include (-
[ CheckTranscriptStatus;
#ifdef TARGET_ZCODE;
return ((0-->8) & 1);
#ifnot;
return (gg_scriptstr ~= 0);
#endif;
];
-).

To decide whether currently transcripting: (- CheckTranscriptStatus() -)

But I’m clueless which bit or number in 0–> or wherever to look at to see which flags are where.

(I’m definitely cool with being pointed to documentation, here. It’ll be good to know where to look next time.)

The fixed-width flag is bit 2 of the second flags byte, so (0-->8) & 2. But it’s deprecated from version 5 onward, which has a different system for text styles that can handle italics, bold, etc instead of just fixed-width or not.

1 Like

I put together an example scenario (for 6M62, so it may need to be adapted). Note that this attempts to avoid any visual interference with output from the player’s perspective.

"Z-Machine Undo Switcheroo"

Place is a room.

[Following all replace their Standard Rules versions...]
To end the story
    (documented at ph_end):
    (- deadflag=3; story_complete=false; set_saved_bit = true; -).
To end the story finally
    (documented at ph_endfinally):
    (- deadflag=3; story_complete=true; set_saved_bit = true; -).
To end the story saying (finale - text)
    (documented at ph_endsaying):
    (- deadflag={-by-reference:finale}; story_complete=false; set_saved_bit = true; -).
To end the story finally saying (finale - text)
    (documented at ph_endfinallysaying):
    (- deadflag={-by-reference:finale}; story_complete=true; set_saved_bit = true; -).

Dying is an action applying to nothing. Understand "die" as dying.

Carry out dying:
    say "OK.";
    end the story.

To signal saved bit: [in case you want to send the signal without ending the story...]
    (- set_saved_bit = true; -).

To decide whether restore message should be modified:
    (- saw_saved_bit -).

[modify the following as desired...]
The immediately undo rule response (E) is "[if restore message should be modified][bracket]Just kidding.[close bracket][else][bracket]Previous turn undone.[close bracket][end if]".

Include (-

! ==== ==== ==== ==== ==== ==== ==== ==== ==== ====
! ZMachine.i6t: Undo (modified)
! ==== ==== ==== ==== ==== ==== ==== ==== ==== ====

Global set_saved_bit;
Global saw_saved_bit;

[ VM_Undo result_code;
    if (set_saved_bit) {
	    $10-->0 = ($10-->0) | 2;
    }
    set_saved_bit = false;
    @restore_undo result_code;
    return result_code;
];

[ VM_Save_Undo result_code;
    @save_undo result_code;
    saw_saved_bit = false;
    if (($10-->0) & 2) {
	    saw_saved_bit = true;
	    $10-->0 = ($10-->0) - 2;
    }
    return result_code;
];

-) instead of "Undo" in "ZMachine.i6t".

This is not Glulx-friendly code.

For details on it, you must ponder the Z-Machine Standards documentation. Look for mentions of “flags2”.

2 Likes