Before Processing a Command: initial feedback

Coming from here

If you ever have “before/after reading a command” rules that are meant to be tied to one specific action, you should try:

  • YOUR ACTION THEN SOMETHING
  • SOMETHING THEN YOUR ACTION
  • YOUR ACTION THEN YOUR ACTION

The first one will usually work, the second one will break if you’re trying to do specific command parsing in an “after reading a command” rule, and the third one will break if you’re resetting variables in a “before/after reading a command” rule.

For another example, Counterfeit Monkey will choke if you WAVE X-REMOVER AT SOMETHING THEN WAVE Y-REMOVER AT SOMETHING, because it uses an “after reading a command” rule to check what letter the remover should be set to. In 99% of cases, “after reading a command” rules are a dangerous trap; they’ll work just barely well enough that you won’t realize all the hidden assumptions they break.

I’ve been frustrated with “before/after reading a command” rules for ages now. They’re a trap that seems intuitively appealing but leads to fragile, hard-to-maintain code…and they’re often the only way to alter Inform’s basic parsing, like Counterfeit Monkey’s letter-remover. (Anything with regexes, really.) This even causes problems with one of the documentation’s own examples, The Left Hand of Autumn, which resets a parsing flag before reading a command (and thus not in between parsing multiple actions).

So why not just make a better way? It turns out spite is a great motivator, and I reinstalled Inform to throw together a quick fix for this.

Before Processing a Command-v1.i7x (34.0 KB)

Note: this extension only works on Z-machine at the moment, and only in Inform 10. Both of these shouldn’t be too hard to fix. But I wanted to get some initial feedback before sinking more time into this.

This extension allows code like this:

Include Before Processing a Command by Daniel Stelzer.

Before processing a command: say "Command: [player's command][line break]".

Lab is a room.

>jump then x me. east
Command: jump then x me. east

You jump on the spot.

Command: x me. east

As good-looking as ever.

Command: east

You can’t go that way.

It’s not a universal solution, but it should fix most of the problems with “before/after reading a command” and multiple actions. In particular, it’ll fix the problem with The Left Hand of Autumn and multiple multi-examining actions, or Counterfeit Monkey and multiple letter-remover resets.

What do you think?

6 Likes

Turns out it took very little work to make it support 6M62 and Glulx, in the end. So give this a try and see how it feels!

This extension deliberately does not support “reject the player’s command” in “before processing a command” rules, to discourage people from reimplementing action processing this way, but that can be added if there’s a good enough reason for it. It does support “change the text of the player’s command to…”, which will affect the current action and everything after it in the line.

10.1: Before Processing a Command-v1.i7x (34.0 KB)
6M62: Before Processing a Command.i7x (6.0 KB)

3 Likes

Ah right. You mean THEN as in the divider between sequential actions given in one line?When you first posted this list in the other topic, I didn’t get it. This extension looks pretty cool.

I’m semi-glad I’ve been oblivious, as the whole sequential actions thing seems to open onto a horrible world. I’d never thought of having to program for it before.

Like you, I wouldn’t want to turn off the player’s possibility to cue up many actions in one line unless I had to… which for my WIP, I do have to, since otherwise a ton of Unified Glulx Input would have to be rewritten to cover cases where the input type was switched mid-sequence.

-Wade

1 Like

Unified Glulx Input, really? The whole point is that UGI operates on one input at a time.

Or do you mean a situation where one parser command switches the game into a different mode where (e.g.) it is now taking keystroke inputs as commands? I would just break the action sequence at that point. It’s a “catastrophic event”, like dying.

Yeah, that might honestly be a good reason to allow “reject the player’s command” in these rules. If the “before processing a command” rules run when the input type has changed, reject it completely.

Yeah. Like you’ve queued up ‘W, BUY DRINK’, and upon going west, a character interrupts you and the game changes to a choice-based conversation. Then BUY DRINK goes nowhere, and I forget if the game freezes or if it just looks a bit silly.

-Wade

Here’s where we all talked about this six years ago.

-Wade

Are you able to add hooks for that?

-Wade

I’ve now made it so that “reject the player’s command” works as you’d expect in these rules: it shuts down all further processing and returns to the prompt. I also threw together an example for how this might be useful.

"The Riddle of the Labyrinth"

Include Before Processing a Command by Daniel Stelzer.

[We want to interrupt ongoing commands if something dramatic has happened since the last time the player typed something on the keyboard.

We handle this with a flag: it gets set to false whenever the player has the opportunity to type, and set to true whenever dramatic changes happen in the world.

If the flag is true "before processing a command", then, we want to cancel all processing. This is where "reject the player's command" comes in.]

The interruption flag is initially false.
Before reading a command: now the interruption flag is false.
Before processing a command when the interruption flag is true:
	say "(Since something dramatic has happened, the remainder of your command---'[player's command]'---has been cut off.)[paragraph break]";
	now the interruption flag is false;
	reject the player's command.

To interrupt ongoing commands:
	now the interruption flag is true.

[Now we contrive a reason why the player would want to type a long sequence of commands on a single line, but might also get interrupted in the process.

This is a sort of maze that was very popular in the early 80s, where every wrong step leads back to the starting location. The intended result is that you need to know the correct path in advance. But what usually happened instead was that players would map it by brute force.]

Definition: a direction (called the way) is viable if the room-or-door (way) from the location is not nothing.
A maze is a kind of room. The printed name of a maze is "Labyrinth". The description of a maze is "Exits lead [list of viable directions]."

Start is a maze. The description of Start is "You've chalked a big X on the floor here to mark your starting point. Exits lead [list of viable directions]." North of Start is Start. East of Start is Maze1. West of Start is Start. South of Start is Start.

Maze1 is a maze. North of Maze1 is Maze2. East of Maze1 is Start. West of Maze1 is Start. South of Maze1 is Start.

Maze2 is a maze. North of Maze2 is Maze3. East of Maze2 is Start. West of Maze2 is Start. South of Maze2 is Start.

Maze3 is a maze. North of Maze3 is Start. East of Maze3 is Maze4. West of Maze3 is Start. South of Maze3 is Start.

Maze4 is a maze. North of Maze4 is Start. East of Maze4 is Maze5. West of Maze4 is Start. South of Maze4 is Start.

Maze5 is a maze. North of Maze5 is Start. East of Maze5 is Start. West of Maze5 is Start. South of Maze5 is Maze6.

Maze6 is a maze. North of Maze6 is Start. East of Maze6 is Start. West of Maze6 is Maze7. South of Maze6 is Start.

Maze7 is a maze. North of Maze7 is Start. East of Maze7 is Start. West of Maze7 is Start. South of Maze7 is Start. Southeast of Maze7 is End.

End is a room. The description of End is "That last passage dropped you in a dead end with no exits. How can you escape now?" Northeast of End is nothing.

[And our source of interruptions!]

The Minotaur is a man.
Instead of jumping in the presence of the Minotaur:
	say "You jump on the spot. The Minotaur is startled and flees!";
	now the Minotaur is nowhere.
Instead of doing anything in the presence of the Minotaur:
	say "The Minotaur charges. The results are unfortunately too gory to describe in text.";
	end the story saying "You have died".

Every turn:
	if a random chance of 1 in 5 succeeds:
		move the Minotaur to the location;
		say "The Minotaur lumbers into the room!";
		interrupt ongoing commands.

[We don't really want to make the player brute-force the maze.]

The player carries a clue. The description of the clue is "Instead of the traditional spool of thread, the king's daughter gave you this slip of paper. On the front it says: 'E. N. N. E. E. S. W. SE. Then say the magic word.' On the back it says: 'Jumping scares the Minotaur away.'"

[And the final magic word. SAY XYZZY will, by default, map to "answering yourself that xyzzy".]

Instead of answering yourself that a topic:
	say "You cry out, '[topic understood]'! But there is no answer."

[The correct path through the maze has drawn out this magic letter on the map.]

Instead of answering yourself that "r" when the location is End:
	say "As you call out the magic word, a hidden door swings open in the wall...";
	end the story finally saying "But what happens next?".

Results:

Labyrinth
You’ve chalked a big X on the floor here to mark your starting point. Exits lead north, south, east and west.

>i
You are carrying:
a clue

>x clue
Instead of the traditional spool of thread, the king’s daughter gave you this slip of paper. On the front it says: “E. N. N. E. E. S. W. SE. Then say the magic word.” On the back it says: “Jumping scares the Minotaur away.”

>e. n. n. e. e. s. w. se.

Labyrinth
Exits lead north, south, east and west.

Labyrinth
Exits lead north, south, east and west.

Labyrinth
Exits lead north, south, east and west.

Labyrinth
Exits lead north, south, east and west.

The Minotaur lumbers into the room!

(Since something dramatic has happened, the remainder of your command—“e. s. w. se.”—has been cut off.)

>jump
You jump on the spot. The Minotaur is startled and flees!

>e. s. w. se.

Labyrinth
Exits lead north, south, east and west.

The Minotaur lumbers into the room!

(Since something dramatic has happened, the remainder of your command—“s. w. se.”—has been cut off.)

>jump
You jump on the spot. The Minotaur is startled and flees!

>s. w. se.

Labyrinth
Exits lead north, south, east and west.

Labyrinth
Exits lead north, south, southeast, east and west.

End
That last passage dropped you in a dead end with no exits. How can you escape now?

The final command to escape is left as an exercise for the reader.

Before Processing a Command.i7x (16.5 KB)

What I now need is some testing with UGI, to make sure I didn’t break that in the process. Once that’s working, I’ll put this on the Github for public use.

2 Likes

Feedback’s been positive, so it’s now in the repository, for both 10.1 and 9.3.

3 Likes

@Draconis, I’m thinking about adding this extension to a game that uses a plural-examining action similar to The Left Hand of Autumn. Do I need to do anything other than just including the extension, and testing the scenarios you mentioned here?

Include the extension, put your rules in the “before processing a command” rulebook, and test those cases, yeah. @Zed proposed some improvements in DMs for cases I hadn’t thought about much when first writing this, which I’m going to add to the repository at some point; is this for an ECTOCOMP game, or is your deadline farther away?

3 Likes

It’s an already-released game that I was doing bug fixes on, so there’s no deadline. I wasn’t sure whether to add the extension, in case I end up breaking something. I haven’t looked into how to move existing rules into the “before processing a command” rulebook.

Oh–if I’m using another extension (a hint extension) that uses “after reading a command” rules, would I also need to edit the hint extension to put those rules into a different rulebook?

1 Like

It depends what the rules are used for. If it’s something like removing stray periods and apostrophes (to keep TALK TO MR. BIGGS from being read as two commands), that’s fine; if it’s actually trying to parse commands in “after reading a command” rules, you’ll want to change them.

This extension doesn’t get rid of the “after reading a command” rulebook or anything; it just adds a new hook that’s a better place for those rules in most circumstances.

1 Like

This is really important. The sensible domain of After reading a command (or Before Processing a command in the forthcoming revision Daniel intends) is examining the player’s command to possibly use change the text of the player's command to [...] or reject the player's command (thereafter, there’s no point to either of these.[1]).

Don’t, for instance, try [action] in after reading a command… instead, change the text of the player's command to something that will result in that action being attempted.

Use it to set up the parser for success, but let the parser and the rest of the turn sequence, (like action processing) do their jobs. The various parts of the turn sequence involve / create / rely on a whole lot of interlocking assumptions about the state things are in at any given moment during said sequence and what has or hasn’t happened and if you’re not working with it, you’re working against it and setting yourself up for problems.


  1. OK, reject the player's command is an alias for rule fails so, strictly, it could be used anywhere that could. ↩︎

2 Likes

Zed’s post here made me go back and review my use of the extension because I have a lot of After reading a command rules in my WIP. I found I’d been well behaved and stuff was working.

However I did discover a minor hiccup with the interruption mechanism. It’s specific to my setup.

My game is using Unified Glulx Input and there are about 10 input modes; a few line, most char-based. Code automatically sets the extension’s ‘interruption flag’ to true if, between two commands, it notices the input mode has changed. This works as intended. It stops the player doing something like chaining 4 commands, then the game crashing when the first command changes from line to char input, for example.

I’ve got a problem case that occurs at the end of almost every chapter, though. The winning command of the chapter usually leads through one or more wait-for-space moments before placing you back in parser mode in the next chapter. The wait for space is this from Unified Glulx Input:

! Wait for space or Enter.
[ InputKeysUntilSpace ch;
	while (true) {
		ch = InputKeystroke();
		if (ch == ' ' || ch == keycode_Return)
			return;
	}
];

Using this, Before Processing rules don’t run during the ‘cut-scene’. This means if the player chains commands atop the winning move, it kinds of wanders into a bit of a no-mans-land. The game successfully reaches the next chapter, but the interruption mechanism never had a chance to go off. The new chapter opens with the ‘I beg your pardon?’ error, so there’s some blob of something that went through to prompt that, and I don’t know how to ‘trim’ it.

I tried using ‘reject the player’s command’ (I know this is just rule fails) after the opening description of a chapter, but it doesn’t stop the ‘I beg your pardon?’ appearing.

-Wade

1 Like

Hmm. Unfortunately Unified Glulx Input is rather out of my depth, but I’ll take a look!

Thanks. I’ve got a feeling it’s more like - if I had a hook to end the chaining process that I could run manually during my ‘space time’ that would do it. That’s what I hoped to do by setting the interruption flag to true, but it doesn’t get a chance to do its thing due to, I think, the very elemental nature of the keypress routine there? However I am, as often, talking semi-blind at the low level stuff :slight_smile:

-Wade

That should be possible; I’m not familiar enough with the internals of UGI to know how it should work, but there are low-level ways to stop the parser from handling the rest of the command (e.g. in case the game ends), and I suspect there are ways to invoke that that will be helpful here.

I’ll just let you know, @Zed made a suggestion in a PM which seems to have fixed my problem. i.e. There is now no more ‘Beg your pardon’ if you chain commands through a chapter transition and space-barring cutscene.

This worked for me when I zeroed the held-back-mode variable after my manually created cutscene. i.e. Even though I wasn’t using Inform’s scenery mechanism.

-Wade

3 Likes