It looks like when a player enters multiple commands separated by periods, code like this
if the player's command matches the text "whatever"
doesn’t work the way I expected.
“The player’s command” seems to include all of the commands at first, and then as each command is run, that command gets deleted.
Sample code:
Lab is a room.
A frog is here.
Every turn:
say "Player's command: [player's command][line break]";
if the player's command matches the text "frog":
say "The text 'frog' is matched.";
otherwise:
say "The text 'frog' is not matched.";
Test me with "x me. x frog. i."
Output:
Lab
You can see a frog here.
>test me
(Testing.)
>[1] x me. x frog. i.
As good-looking as ever.
Player's command: x me. x frog. i.
The text "frog" is matched.
You see nothing special about the frog.
Player's command: x frog. i.
The text "frog" is matched.
You are carrying nothing.
Player's command: i.
The text "frog" is not matched.
Is there a straightforward way to check for matches in just the command that’s currently being run? So “x me” would not match “frog,” “x frog” would match “frog,” and “i” would not match “frog”?
Unfortunately not by default. That’s one of the big issues with accessing the player’s command directly: you don’t have access to all the work the parser’s already done on it.
At what stage of the turn do you want this information? If it’s after the “generate action rule”, it’s possible to get the data out of the parser’s internal registers. Before that, the parser hasn’t figured it out yet.
Mostly I was trying to use matching in “Does the player mean” rules, but also in a “For supplying a missing noun” rule and for understanding an action.
Would it work to check “the player’s command” for command separators (periods or the word “then”–I’m not sure if there’s anything else) and create a new string consisting of only what comes before the divider, just for matching purposes? (I suppose it could complicate things if a player tried to use a period in another way–for example, in a title like “Ms.”–but that’s unlikely to come up in any of the matching I’m trying to do.)
Include Before Processing a Command by Daniel Stelzer.
[When a player enters a list of commands on the same line, separated by periods or "then," "the player's command" will initially include multiple commands. I'm using "the current command" to mean only the one currently being run.]
The current command is a text that varies.
Before processing a command (this is the find the current command even if the player has entered a list of commands separated by periods or thens rule):
if the player's command matches the regular expression "(.+?)(\.|\bthen\b)(.+)":
now the current command is the text matching subexpression 1;
otherwise:
now the current command is the player's command.
Lab is a room.
A frog is here.
Every turn:
say "Player's command: [player's command][line break]";
say "Current command: [current command][line break]";
if the current command matches the text "frog":
say "The text 'frog' is found in the current command.";
otherwise:
say "The text 'frog' is not found in the current command.";
Test me with "x me. x frog. i."
(I haven’t tested this with any of my original rules, e.g. “Does the player mean” rules. I also don’t know if it’s likely to cause other problems, like slowing things down unnecessarily.)
Output:
Lab
You can see a frog here.
>test me
(Testing.)
>[1] x me. x frog. i.
As good-looking as ever.
Player's command: x me. x frog. i.
Current command: x me
The text "frog" is not found in the current command.
You see nothing special about the frog.
Player's command: x frog. i.
Current command: x frog
The text "frog" is found in the current command.
You are carrying nothing.
Player's command: i.
Current command: i.
The text "frog" is not found in the current command.
the current command is printing in lowercase even if I include uppercase letters in the original command. Which is fine. But can I rely on the current command being in lowercase, or when I match against it, should I match case insensitively just in case?
Also, should this rule be first (that is, a “First before processing a command” rule) or does that matter? The code I’m modeling this after uses a “First after reading a command” rule.
Are you compiling to Z-machine? If so, all commands are converted to lowercase by the Z-machine interpreter; unfortunately there’s no way to get around that, it’s just how the platform works. On Glulx, casing should be preserved.
Whether this should be a “first” rule depends on whether you have any other “before processing a command” rules, and if you want this rule to run before or after them. If you have other “before processing a command” rules which will alter the player’s command, for example, and you want the current command to include those alterations, this should probably be a “last before processing a command” rule.
It looks like when I use a test script, uppercase text gets automatically converted to lowercase when the command is entered.
But when I enter commands directly, case is preserved.
So…maybe I should go back to my “Does the player mean” rules, and anytime I try to match text, I should add “case insensitively.” I’m not sure if that’s better or worse than altering all commands to lowercase to begin with.
I’ll post this here since it’s what I ended up with. This code creates a lowercase version of the player’s command to be used for text matching. If there are multiple commands in the same line, it splits off just the first of the commands.
It turns out the regular expression matching line that I had borrowed from this post matches “then” but not “THEN” which is one reason I decided to put the command text in lowercase before doing any matching. Since I don’t need to print out the “current command” anywhere, it shouldn’t need to match the player’s capitalization.
I’m also blocking commas elsewhere in my code, so those can’t be used as command separators.
(Edit: If a period is used in a command for some other reason than command separation–for example, as part of a person’s title–I guess that could make the “current command” inaccurate, but I don’t anticipate that happening with this game, or causing any dire consequences if it does, since I’m using the “current command” text mostly for disambiguation purposes.)
Include version 2/260110 of Before Processing a Command by Daniel Stelzer.
The current command is a text that varies.
Last before processing a command (this is the find the current command rule):
let lowercase-player's-command be "[player's command]" in lower case;
[If multiple commands separated by "." or "then" are detected, find the text that comes before the first separator:]
if lowercase-player's-command matches the regular expression "(.+?)(\.|\bthen\b)(.+)":
now the current command is the text matching subexpression 1;
otherwise:
now the current command is lowercase-player's-command;
[say "**Player's command: [Player's command][line break]";
say "**Current command: [Current command][line break]";]
You might be able to get away with something simple like:
To decide which snippet is the current command:
(- (99+wn) -).
which can then be used like the player's command.
This has the advantage of using the actual parsing activity to determine the end of the command, so it won’t be confused by something with “then” in its name.
Before parsing has finished, it might be less reliable for a given purpose.
A snippet is defined as a span of words from player input. It encodes two pieces of information with formula
(startingWord*100)+numberOfWords
The variable wn is a key global variable of the parser, indicating the next word in the command to be processed. When parsing is done, it will have been advanced past the end of the command.
The expression used is just a simplification of 100+wn-1.
Ah! I assumed there might have been something here which was narrowing down a single chunk of a multi-stage command, but I guess it’s just taking advantage of the usual internal systems in a way that “the player’s command” doesn’t.
I’m interested in this because it potentially solves the last problem of all this chained-command-dancing-around (which both bg and I are playing with atm). Which is that commas can’t be relied upon to be command separators, unlike periods and THENs. So if I could pull out the first command from a line containing many, using this snippet dealie, it would handle commas correctly, which doing the same by regex cannot.
However, I haven’t found a way to use this. If I refer to the current command as generated by the To decide… phrase, in any way during After reading a command rule, it gives
**** Run-time problem P39: Attempt to say a snippet value which is currently invalid: words 0 to 98*
I tried matching it to text as is (didn’t expect that to work anyway, because it’s a snippet), putting it in a variable first, and putting it in a variable the inverted commas way (now test-cmd is the substituted form of “[current command]”).
The wn variable is set by the parser, so this value is only usable after the parser has finished its work—meaning the “generate action rule” has run, and the action has been stored in “the current action”, “the noun”, and so on.
Ah okay. So it might be handy for doing text matches in check rules and such without having to do regex chopping-up first. But not for what I’m mostly doing, which is brutally interfering with commands at the after reading / before processing stages.
otis did allude to to this-
Before parsing has finished, it might be less reliable for a given purpose.
he just did it with cute understatement that I didn’t take seriously
It’s one of Inform’s most deep-rooted problems: it’s really hard to tinker with the parser without breaking something in the process. So people end up working around the parser in various ways (like “after reading a command”), and the result tends to be quite fragile. There are 90s-era hacks for specific circumstances buried deep in the internals, which are now effectively impossible to remove, and it’s equally impossible to add new ones for your own game’s circumstances.
I keep hoping one of the updates will shift more of the machinery into author-accessible I7, but it’s a massive undertaking. I wouldn’t expect it any time soon. “Before processing a command” is a bandaid at best, and I’m still not sure if it’ll hurt more than help.
It’s helped a ton already! It’s just that as soon as people like yourself build a road, people like me fill it with traffic and then try and get a bus to merge.
And I don’t consider that a failing on your part, to be clear! I hate how fragile most “after reading a command” rules are, but they’re fragile because the only tool Inform offers for interfering with the parser inherently makes them that way. “Before processing a command” makes them a little less so, but I wish Inform just provided better tools for this purpose in the first place.
There’s a certain level of historical irony here, since one of the selling points of Inform over TADS (2) when it first came out was that Inform’s parser was written in library code, so you could theoretically modify it, whereas TADS’ parser was built into the interpreter. Fast forward a couple of decades and TADS 3 has moved the parser into the library, whereas Inform still has the same parser it did before but has embedded it so deeply into whatever it is that the I7 compiler actually does that it’s impossible for most people to consider tinkering with it.