I’ve been wondering if it would be possible, in Inform 6, to allow a player to type JUMP, PAUL in addition to PAUL, JUMP. It seems like the sort of thing that someone new to IF would try and would be annoyed when it didn’t work. I have tinkered a little but gotten nowhere. Does anyone have any ideas?
I would probably start with an “after reading a command” rule. Use regular expressions to split the player’s command into before-comma and after-comma chunks, check to see if the after-comma chunk matches a person in scope, and if it does, reconstruct the player’s command with the the two chunks switched.
Also remember that the player’s input might contain multiple commands with separators in between like . or THEN.
And that it’s legal (though rare) to use commas in something like “take ball, bat and glove”. (If you want to allow the serial comma in there you have to do this.)
Also, the comma itself can be used as a command separator, so that “JUMP, JUMP” will have the player jump twice. (I think this may also be pretty rarely used.) Fortunately here you probably only have to worry about the bit after the last comma–a player who types “jump, paul, look, jump” deserves whatever happens to them.
Ok, the right way to do this is probably to delve into the code for snippets and figure out how to winkle out the part after the comma so you can use the "if (snippet) matches “[someone]” functionality to test whether it’s the name of a person. This is probably not that hard if you know how snippets work on the inside. I don’t know that, so I present this laughable kludge (which is necessary because you can’t test for commas in “If the player’s command matches…”):
After reading a command: if the player's command includes "qwe3ia": say "You typed 'qwe3ia'? Really?"; reject the player's command; let cached command be the substituted form of "[the player's command]"; let temptext be the substituted form of "[the player's command]"; if temptext matches the regular expression ",<^,>*$": [this selects the last comma and everything after] let postcomma be the text matching regular expression; replace the regular expression ",<^,>*$" in temptext with ""; [this cuts the last comma and everything after] replace character number 1 in postcomma with ""; change the text of the player's command to "[temptext] qwe3ia [postcomma]"; [now we've replaced the comma with "qwe3ia"] if the player's command includes "qwe3ia [someone]": change the text of the player's command to "[postcomma], [temptext]"; otherwise: change the text of the player's command to "[cached command]".
This is mostly presented as an exercise for me–there must be a better way.
EDIT: And now I realize that the OP clearly said “Inform 6.” Sorry!
Yeah, I missed the I6 also.
Well, you could implement your own I6 equivalent to ‘after reading a command’.
You’d need to modify the parser (in parser.h in the latest revision of inform6lib, parserm.h in earlier ones).
You’d want to stick your hook after the call to Keyboard() in parser part A:
.ReType; Keyboard(buffer, parse); [Infix stuff elided.] .ReParse;
and modify the input and parse buffers however you want.
For the Z-machine input and parse buffer format, see the @read opcode in the zcode spec or §2.5 of the DM4 (Reading into arrays from the keyboard). Be sure to look at the DM4 errata for §2.5, because there’s an inconsistency in the text/diagram.
Glulx is similar, but slightly different. See the “input buffer and parse table” section at the end of the glulx inform technical reference.
To make a general replacement for after reading a command, you’d also need to make some changes to NounDomain, since, in the event of ambiguity, it asks the player clarifying questions, writes the player’s answers into the input and reparses it. You’d want to call your hook everywhere that NounDomain returns REPARSE_CODE (either in NounDomain before it returns it, or at the call sites before it jumps to the .ReParse label). In this specific case, it might not be necessary.
Mmm, and for anyone working out my hack, it encounters the same kind of problem with disambiguation:
I think the kind of solution you’d have to implement here involves short-circuiting disambiguation, and short-circuiting disambiguation always seems to involve hacking the parser, unfortunately. (Or maybe there’s a simpler solution, but it’s past my bedtime.)
Now that we’re talking about I7, the Reversed Persuasion Correction extension (by me) is made just for this. It’s in the Public Library. It uses matt w’s approach but with a lot of checks for special cases like disambiguation and giving multiple commands at the same time.
(Sorry to derail the thread, but perhaps the I7 code logic helps with making the I6 version…?)
Yeah I think so. After the ‘after reading a command’-equivalent I6 hook is invoked, there’s still the matter of what actually to do. I think that the I7 suggestions in this thread would be helpful there.
I6 doesn’t have regex support, but we’re just looking for commas in the input, and comma is its own dictionary word, so it shouldn’t be a problem.
Tests like ‘if the player’s command includes “[someone]”’ are more of an issue. I guess we’d need to call ParseToken repeatedly at different positions in the parse buffer trying to match a CREATURE_TOKEN. It’s a little more constrained if we’re looking for “qwe3ia [someone]”, since we have specific positions (after occurrences of ‘qwe3ia’) where we know to look for the match.
The cool thing is that we can write the I7 code, look at what I6 code is generated, and take hints from that.