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?

4 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

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.

1 Like

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

2 Likes