Message Parameter Substitutions
Adapted for Adv3Lite by Eric Eve
If you've looked at the adv3Lite library (or indeed, at some of the other chapters in this Technical Manual), you've probably noticed some funny-looking syntax in many of the response message strings, where certain words are enclosed in curly braces. Things like this:
{I} {see} nothing unusual about {it dobj}.
You obviously wouldn't want those curly braces and slashes and so on to show up in the actual output that the player sees, and indeed they don't, so you can probably guess that the curly-brace parts are placeholders that the library replaces with something appropriate to the situation. You can probably also get a pretty good idea of how to use these special strings in your own messages just from reading enough of the library messages and piecing together the pattern. This article is designed to help you understand the details of the message parameters and how to use them.
An example
The full explanations later in the article might get a little abstract, so let's start with a simple example and see how it works. The format strings were meant to be fairly intuitive just looking at them; if we can understand how they're actually used in practice, it should help with the more abstract material below.
Let's use the same example we saw earlier:
{I} {see} nothing unusual about {it dobj}.
For starters, we know that the parts in curly braces are the substitution strings, so we have "{I}", "{see}", and "{him dobj}". The library replaces each one individually, so let's look at them one by one.
{I}. Whenever the library sees a substitution string that uses a first-person pronoun, it replaces it with a word that relates to the actor carrying out the current command. ("First-person" is the grammatical term for words that refer to the person uttering a sentence: "I", "me", "myself", "my", etc.) The library uses this convention simply because it's clear which role the first-person pronouns refer to ("I" must be the subject of a sentence or "me" the object) whereas we can't tell whether "you" is meant to be the subject or the object. Note that the first letter is upper-case: this tells the library that the string it substitutes should also be capitalized.
{see}. "See" is an irregular verb which Adv3Lite knows how to conjugate (for example, that it becomes "saw" in the past tense). If we'd used a regular verb like "observe" we'd have written "observe(s/ed}" This is a parameter that the library replaces with either an "s" or nothing, as needed to make the verb agree with the subject. So, if the library replaces the "{I}" with "You", it'll replace the "{s}" with nothing, because "you see" is the correct subject-verb agreement; if "{I}" is replaced with "Bob", then "{s/ed}" will be replaced by "s", because in this case we'd want the sentence to start "Bob observes". The "ed" part of {s/ed} would give us "observed" if the game was narrated in the past tense.
{him dobj}. The "him" tells the library that you want to use a pronoun here. The "dobj" says that you want that pronoun to be the appropriate one for the direct object of the current action ("dobj" stands for direct object). We use "him" rather than "it" so shat the library will can tell that you specifically want to use an "objective case" pronoun. "It" wouldn't doesn't carry enough information here, since the same word "it" works equally well in nominative and objective cases in English. An "objective case" pronoun, by the way, is one that's used as the object of a verb rather than as the subject. "He" can only be the subject, which is called "nominative" case, and "him" can only be the object of the verb: "he pets the dog" and "the dog bites him."
Note that the "dobj" in {him dobj} does not indicate the grammatical function of the word in the sentence being displayed. Rather, it selects the direct object from the current action. For example, you could have written "{He dobj} look{s/ed} perfectly ordinary," in which case we're still using "dobj" even though it's now serving as the subject of the displayed sentence. Note that "dobj" has stayed the same, but we're now using "he" to tell the library that this word is to be in the nominative case, since it's the subject of the sentence.
You might be wondering why "{I}", {see} and "{s/ed}" stand on their own, while "{him dobj}" required the "dobj" part to tell us which command object we're talking about. The reasons are different for the two cases. For "{I}", there's no object specified because the "I" part implies that the command's actor is the object. In the case of "{s/ed}", we omit any object reference because we simply want to use the same object as we did for "{I}"; when the object is missing, the library uses the same object it used for the immediately preceding substitution.
That should give you a concrete reference point for how these strings are used. Now we'll go into the details.
The problems that parameters solve
At first glance, you might wonder why the library goes to such trouble to use these message parameters, rather than just using the literal text. The answer is that the message parameters make it possible for a single library message to be re-used in several different contexts.
First, parameterized messages make it possible for the same message to describe an action by the player character or by a non-player character. For the most part, the adv3Lite library tries to treat player and non-player characters as interchangeable; when processing a command, for example, the library doesn't make any assumption that the player character is the one performing the command. If the messages all said things like "You open the flask," though, it would be almost useless to have such actor independence in the rest of the library: we'd carry out an NPC's actions correctly, but the descriptions would be all wrong.
Substitution parameters help a lot with this. Rather than saying "You open the flask," we say "{I} open{s/ed} the flask". The "{I}" turns into "You" if the player character is doing the opening, or "Bob" if Bob is. Likewise, the "{s/ed}" turns into an empty string for the player character, because the correct verb agreement for "you" is "open" with no suffix; but if Bob is doing the opening, the "{s/ed}" turns into "s", so we have the proper subject-verb agreement, "Bob opens".
Second, parameters make it a lot easier to plug in the names of the other objects apart from the actor performing the command. Rather than having to call some method on the indirect object, we can simply plug in a curly-brace string with the special identifier "dobj". There's also an "iobj" identifier for the indirect object. What's more, we can define our own additional parameter names and associate them with any objects we want. So, we can simply say something like "{I} open{s/ed} {the dobj}", rather than calling some method on the flask object to get its "the" name. (Internally, of course, the library does call a method on the flask object to get its "the" name. It's just that it's less work for us to write it with the curly braces.)
Third, the parameters make it easy to switch the "person" of the narration. Since we've already parameterized the subject and verb for each message that refers to an actor performing a command, we can easily tell the player character Actor object whether we want it to refer to itself as "I", "you", "he", "she", "we", "they", or whatever else. By default, the library uses the second person, "you", but that can be changed simply by changing a property (person) of the player character's Player or Actor object.
Fourth, the parameters make it easy to switch the "tense" of the narration.
Format of parameter strings
As we've already seen, substitution parameters are enclosed in curly braces, "{ }". The library takes out everything between and including the curly braces and replaces it with the appropriate substitution string.
Within the braces, the format is fairly simple. There's always a "format type" string, and sometimes there's an "object selector" string.
The "format type" string tells the library the precise grammatical function of the word you want to use in the final string. When the library was designed, one possibility we thought about was using a grammatical term, like "nominative-noun" or "objective-pronoun"; but that would have been a lot of work to keep straight, so instead we chose to use essentially an unambiguous example of the word or phrase desired. For example, instead of something like "{nominative-pronoun}", the library uses "{I}" or {he} because they're both unambiguous examples of nominative pronouns.
The "object selector," if it's present, is separated from the format type by a space. This string indicates which of the current action's objects is being referred to. The object selector is optional for two reasons. First, some of the format types imply an object selector; in particular, all of the first-person types ("I", "me", "I'm", and others) imply that we're referring to the target actor of the current action. Second, when a format type doesn't imply an object, then the default object in the absence of an object selector is the same as the object from the immediately preceding substitution string. This is often handy for noun-verb agreement: we specify an object selector for the noun, and then we don't have to specify it again if the verb closely follows because we know we'll get the same object again.
Capitalization
Capitalization of the replacement text follows capitalization of the format string text. If the first letter of a substitution string is capitalized, then the replacement text will have its first letter converted to upper-case. If the first two letters of the substitution string is capitalized, then the entire replacement text will be converted to upper-case.
Reflexives
In English, if the same thing is referred to as both the subject of the sentence and as an object of a verb phrase, then the second appearance of the thing is usually phrased as a "reflexive," which is one of the pronouns ending in "self": yourself, himself, itself, and so on. For example: in "Bob slapped himself with the herring," Bob is both the subject of the sentence and the direct object of the verb, so the direct object is phrased reflexively.
The library tries to make sure that reflexives are used when appropriate. To do this, it keeps track of several things.
- First, the library keeps track of when a substitution string is the subject of the sentence. It does this by keeping a "subject" flag for each format type. Whenever a subject appears, the library remembers the target object used in the replacement (the actor, the direct object, etc).
- Second, for each format type, the library keeps a separate "reflexive pronoun" property. For many format types, a reflexive pronoun wouldn't make any sense, so many of these are simply nil. For format types where a reflexive pronoun makes sense, though, the library keeps the property that can be used to obtain the reflexive form of the substitution.
- Third, whenever a substitution string is not marked as a subject, the library checks the target object to see if it matches the subject object the library has been remembering. If it's the same object, then the library checks the reflexive pronoun property for the format type; if it's not nil, then the library uses the reflexive form of the name, otherwise it just uses the normal substitution.
- Finally, the library tries to keep track of sentence boundaries by watching for sentence-ending punctuation. Whenever the library sees a period, question mark, or exclamation mark, it assumes that the last sentence has ended, so it forgets about any subject it had been remembering.
Pre-defined object selectors
The English library defines the following object selectors:
Selector String | Object |
---|---|
actor | The actor performing the command. This is the actor to whom the command is directed, which is normally the player character unless the player explicit used the "actor, do this" phrasing. |
dobj | The direct object of the command. |
iobj | The indirect object of the command. |
cobj | The 'current' object of the command. Useful when the object may be in either role. |
Note that literal phrases and topic phrases are not available as message substitution parameters. These aren't available because they're not represented as game objects, and hence don't have pronouns and so forth associated with them.
Game-defined object selectors
It's sometimes convenient to add your own special object selectors for a particular message. This is handy when you need to refer to an object that isn't actually part of the command but is related in some way to a command object, such as the direct object's container.
The object selectors are managed by the current Action. The Action class lets you to register arbitrary new object selectors with the setMessageParam() method:
gAction.setMessageParam('myobj', bob);
Note that the parameter is registered directly with the current Action, so it only lasts as long as the current command is being processed.
The library provides a convenience macro that makes it a little less work to register your own selector, but it requires that you have a local (or parameter) variable containing the object you want to register. If you have your object in a local variable, you can do this:
local myobj = bob; gMessageParams(myobj);
This registers the object currently stored in the local variable, and it uses the local variable's name as the object selector. So, once you've done this, you can use your selector in a message string:
return 'It would be pointless to slap {that myobj}. ';
The gMessageParams() macro can register more than one variable at a time; just list the variables to register, separated by commas. This macro simply calls gAction.setMessageParam(), so it's not doing anything special, but it makes the code a little more compact.
For the table of Message Substitution Parameters see the Message Substitution Parameters subsection of the Adv3Lite Library Manual. (There seems little point in repeating that information here, and doing so would risk errors arising from need to keep the two lists in sync).
Timing of parameter substitution
Substitution parameters are handled at three points.
Whenever text is output using say(txt) or a outputting a double-quoted string.
Whenever a BMsg is evaluated (for example by referencing a property on which a BMsg is defined.
Whenever code refers to a list (such as the eventList property of an EventList) of strings containing parameter substitutitions.