[I7] Turning a blank command into an action

I want to turn a blank command into a going action. (The player can type “go to X”, which move him one room towards X. Pressing enter should take another step towards X.)

Easy! You just steal some code from Aaron’s Smarter Parser extension:

Rule for printing a parser error when the latest parser error is the I beg your pardon error (this is the do something useful with blank lines rule): say "(going towards [location-to-go])[line break]"; try going to location-to-go;

Except that a parser error loops you back to the parse command rule immediately, skipping the every turn rulebook and all other rules in the turn sequence rules. It is absolutely essential for my game that this does not happen. Is there a way to turn the blank command into a normal action running through the normal turn sequence rules?

I have one that I got off this board. Actually, I thought you wrote the code :stuck_out_tongue: . Maybe I was mistaken?

In any case, I have a block of I6 code and a few lines of I7 code - which I got off some nice person - will allow you to reroute a blank hit of the enter key to a particular command of your own.

The person who provided the code made that command ‘look.’ For me, I made it ‘again’, because I like hitting return to repeat my last command.

Here are the bits:

[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
	!if (nw == 0) {
	!	@push etype; etype = BLANKLINE_PE;
	!	players_command = 100;
	!	if (ForActivity(PRINTING_A_PARSER_ERROR_ACT) == false) L__M(##Miscellany,10);
	!	@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) { L__M(##Miscellany, 14); continue; }
		if (nw == 1) { L__M(##Miscellany, 15); continue; }
		if (nw > 2) { L__M(##Miscellany, 16); 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, "^";
		L__M(##Miscellany, 13);
	return nw;

]; -) instead of “Reading the Command” in “Parser.i6t”.[/code]

Now the I7 bit.

After reading a command (this is the blank line = again rule): let T be indexed text; now T is the player's command; if T is "", change the text of the player's command to "again".

So just replace ‘again’ with the command you want to have activate, and that’s it. There’s nothing you need to change in the i6 part.

Thank you for asking this question, so I didn’t have to. After spending some time implementing this behaviour in TADS 3, this was something I expected to be trivial in I7.

Nice to see that intuitive natural language at work… :open_mouth:

Does Smarter Parser deal with the issue Victor identified? That is, does it find a way to initiate the substitute command in the normal turn sequence, rather than within the parser error sequence? If not, this would also qualify as a bug in Smarter Parser.


Wow, in that case I am both smart and forgetful. :stuck_out_tongue: Well, whatever its source, thanks for posting the code!

Welcome to the world of Inform 6 inclusions! To be fair, though, you don’t need them often, and only for weird stuff like messing with the parser. (That bunch of code you see is just one of the parser routines copied out verbatim with one small change applied.)

No, I think this is a bug in Smarter Parser. I’ll look it up and mail Aaron, unless he sees this topic first.

It’s not a bug, so much as that there are lots of extensions that interfere with the Keyboard routine, and I’m very hesitant to replace it in an extension I hope to be as compatible as possible.

I should perhaps be much clearer in the Smarter Parser documentation that the non-intrusive method used will bypass the normal turn sequence.

Hm. In the sense that it may very well lead to buggy behavior in any game that uses every turn rules (i.e., most games), I think that it is best to consider it a bug. Just check out this thread, with folks trying to figure out how to deal with the issues raised:


(Until Victor saves the day with the hack above, that is.)

The issue you bring up with Keyboard() replacement is a real one, though. Maybe it’s worth asking Graham to make the error check for blank lines (the part that’s commented out in Victor’s hack) subject to a use option, e.g. “Use blank line input” would prevent the error, allowing for blank lines to be handled by “after reading a command”. Alternatively (and far less ideally!), we could try to corral everyone who’s written a Keyboard()-replacing extension to coordinate the same…


A less-eloquent-than-I’d-hoped-for feature request has been created.

Meanwhile, if we can believe Google’s index of inform7.com, the only extensions that appear to replace Keyboard right now are:

Undo Output Control by Erik Temple
German by Team GerX

I’m a little skeptical of this conclusion, but perhaps it’s true. If so, then perhaps y’all have convinced me: I’ll look into making Victor’s Keyboard replacement hack the default for dealing with blank lines in Smarter Parser, perhaps with a use option for the current non-intrusive time-no-passy LOOK version.

Huh, my feeling was also that there more of these; if that’s all there is–great! Anyway, I’ll be happy to patch UOC to make it work with Smarter Parser. If you package all of the related code in its own section, it would be a simple thing for me to duplicate the hack in UOC.