How to repeat last command

I’d like to be able to repeat the last entered comman by simply hitting enter. I can not find how / where to do that in inform 7. I can use up arrow/cursor to scroll back - thats is not the problem - all I want is to be able to do is hit enter and that jmakes the game repeat the last command - is there a way to make this happen?


When you press Enter with no input, that generates the “I beg your pardon?” parser error. So you can create a custom “rule for printing a parser error” that parses the player’s last command again instead.
EDIT: The extension Smarter Parser offers a better way. Simply override the blank line replacement text with “[previous command]”, and add a “before reading a command” rule to put the player’s command into that variable.

There is an AGAIN command (abbreviation G) which is not just hitting the enter key.

You could also enter a chain of commands with periods to repeat…


…which will carry all commands out without the need to hit enter between.

That’s for a player though. Programming wise, do the above.

Doesn’t this require extra hackery to make sure the turn sequence advances?

The blank command handling prints the beg-your-pardon error without advancing to parsing, action, or the turn sequence. So you’d want to change that, yes.

With that change, you’d just replace the blank line with “again” and let the normal parser mechanism take over.

The issue as I understand it is that blank command handling takes place in the “reading a command” activity, the same activity that asks for input, which is why “After reading a command” rules and the like get preempting. (This is not a super popular design decision; I’d also like to see the other stuff that’s hard-coded in there

There was a solution for this as of 6G60. And… ah, since that just involves commenting out the bit of Parser.i6t that handles blank lines and then using an “After reading a command” rule to handle it instead, I can probably do the same thing with the 6L02 parser myself:

[spoiler][code]Include (- [ Keyboard a_buffer a_table nw i w w2 x1 x2;
sline1 = score; sline2 = turns;

while (true) {
	! Save the start of the buffer, in case "oops" needs to restore it
	for (i=0 : i<64 : i++) oops_workspace->i = a_buffer->i;

	! In case of an array entry corruption that shouldn't happen, but would be
	! disastrous if it did:
	a_buffer->0 = INPUT_BUFFER_LEN;
	a_table->0 = 15;  ! Allow to split input into this many words
	#Endif; ! TARGET_

	! Print the prompt, and read in the words and dictionary addresses
	KeyboardPrimitive(a_buffer, a_table);

	! Set nw to the number of words
	#Ifdef TARGET_ZCODE; nw = a_table->1; #Ifnot; nw = a_table-->0; #Endif;

	! If the line was blank, get a fresh line [except that I've commented this out so "after reading a command" can handle it]
	!if (nw == 0) {
	!	@push etype; etype = BLANKLINE_PE;
	!	players_command = 100;
	!	if (ForActivity(PRINTING_A_PARSER_ERROR_ACT) == false) {
	!		PARSER_ERROR_INTERNAL_RM('X', noun); new_line;
	!	}
	!	@pull etype;
	!	continue;

	! Unless the opening word was OOPS, return
	! Conveniently, a_table-->1 is the first word on both the Z-machine and Glulx

	w = a_table-->1;
	if (w == OOPS1__WD or OOPS2__WD or OOPS3__WD) {
		if (oops_from == 0) { PARSER_COMMAND_INTERNAL_RM('A'); new_line; continue; }
		if (nw == 1) { PARSER_COMMAND_INTERNAL_RM('B'); new_line; continue; }
		if (nw > 2) { PARSER_COMMAND_INTERNAL_RM('C'); new_line; continue; }
		! So now we know: there was a previous mistake, and the player has
		! attempted to correct a single word of it.
		for (i=0 : i<INPUT_BUFFER_LEN : i++) buffer2->i = a_buffer->i;
		x1 = a_table->9;  ! Start of word following "oops"
		x2 = a_table->8;  ! Length of word following "oops"
		#Ifnot; ! TARGET_GLULX
		x1 = a_table-->6; ! Start of word following "oops"
		x2 = a_table-->5; ! Length of word following "oops"
		#Endif; ! TARGET_
		! Repair the buffer to the text that was in it before the "oops"
		! was typed:
		for (i=0 : i<64 : i++) a_buffer->i = oops_workspace->i;
		! Work out the position in the buffer of the word to be corrected:
		w = a_table->(4*oops_from + 1); ! Start of word to go
		w2 = a_table->(4*oops_from);    ! Length of word to go
		#Ifnot; ! TARGET_GLULX
		w = a_table-->(3*oops_from);      ! Start of word to go
		w2 = a_table-->(3*oops_from - 1); ! Length of word to go
		#Endif; ! TARGET_
		! Write spaces over the word to be corrected:
		for (i=0 : i<w2 : i++) a_buffer->(i+w) = ' ';
		if (w2 < x2) {
			! If the replacement is longer than the original, move up...
			for (i=INPUT_BUFFER_LEN-1 : i>=w+x2 : i--)
				a_buffer->i = a_buffer->(i-x2+w2);
			! ...increasing buffer size accordingly.
			a_buffer->1 = (a_buffer->1) + (x2-w2);
			#Ifnot; ! TARGET_GLULX
			a_buffer-->0 = (a_buffer-->0) + (x2-w2);
			#Endif; ! TARGET_
		! Write the correction in:
		for (i=0 : i<x2 : i++) a_buffer->(i+w) = buffer2->(i+x1);
		VM_Tokenise(a_buffer, a_table);
		#Ifdef TARGET_ZCODE; nw = a_table->1; #Ifnot; nw = a_table-->0; #Endif;
		return nw;

	! Undo handling

	if ((w == UNDO1__WD or UNDO2__WD or UNDO3__WD) && (nw==1)) {
	i = VM_Save_Undo();
	#ifdef PREVENT_UNDO; undo_flag = 0; #endif;
	#ifndef PREVENT_UNDO; undo_flag = 2; #endif;
	if (i == -1) undo_flag = 0;
	if (i == 0) undo_flag = 1;
	if (i == 2) {
		SL_Location(); print "^";
		! print (name) location, "^";
		IMMEDIATELY_UNDO_RM('E'); new_line;
	return nw;

]; -) instead of “Reading the Command” in “Parser.i6t”.
After reading a command when the player’s command is “”: [your code goes here][/code][/spoiler]

Use this at your own risk, since I don’t understand how this works–and if you’re using the most recent update, you’ll want to find Parser.i6t and copy out the relevant section so you can comment out the blank line handling. (Finding Parser.i6t is nontrivial; on my Mac the path is /Applications/Inform, and you have to ctrl-click on the Inform application and use “show package contents” to get into Inform

Thanks for your ideas but I i have tried a few things and can not work out how to say something like:

every turn:
now last_command is the player’s last command.

( the compiler objects to this)

before it even gets to…

Rule for printing a parser error when the latest parser error is I beg your pardon error:
say last_command

Ideas please

“The player’s command” is the token you want.
Try “replace the player’s command with last_command.”
(I think)

Or replace the beg your pardon error with

Try “[last_command]”.

“Try” needs to be followed by the literal name of an action, not a text string. If you include this extension you can use the phrase “replay the command last command” (which shows the new command in bold italics, as if playing from the Skein) or “silently execute the command last command” (which leaves the command line blank) to accomplish that.