XYZZYnews

Tales From the Code Front

Scissors, Pants, Glasses, and Yahoos

by Andrew Pontious

When I started programming my first TADS game, back in the seventeenth century... well, not that long ago, but it did feel like a long and torturous process. I was a newbie, yet I was determined to include several features that turned out to be in the advanced category. This article deals with one small change I made in game grammar, so that an otherwise unchanged version of TADS 2.2 recognizes "plural-singulars" -- game constructs that are just one object, but are referred to in the plural. This could be for something like scissors, which are one thing in the real world, or for groups of things you want to combine for game convenience, like "twenty yahoos threatening you from the trees."

First off, I'll make one of those plural-singular objects. I'm going to annotate this pretty heavily for the non-TADS user:

scissors: item    // "item" is the TADS class for something you can pick up
    sdesc = "scissors"   // "short" description of object
    adesc = "scissors"   // descrip. used when game wants to say "a ___" 
                         // In this case, we take out the "a" entirely
    noun = 'scissors'   // vocabulary player can use to refer to object
    location = startroom   // where the object is first located
;
This'll get us started. I also code a box in startroom (the specific class is called "container") so you could put things in it.
     > look
     You are in the starting room of your test game.
     You see a box and scissors here.

A valid command at this point, grammatically but not logically, would be:

     > take scissors out of box.

The game responds, sensibly, with:

     The scissors isn't in the box.

Ah ha! Here is the beginning of the problem. I look around to see where this message comes from. It's in ADV.T, a file normally incorporated into every game (similar but not the same as Inform's verblib and grammar and parser) to prefabricate the TADS playing environment, setting up object prototypes like "room," "openable," and "container." At the very top of the hierarchy is the ur-object that virtually every other object is the descendent of, called "thing." All the standard game verbs are coded here, and are inherited by any objects which don't supersede that code in their own definitions. For instance, the code involving the verb "take" for the direct object looks like this:

    verDoTakeOut( actor, io ) =
    {
        if ( io <> nil and not self.isIn( io ))
        {
            caps(); self.thedesc; " isn't in "; io.thedesc; ". ";
        }
        self.verDoTake(actor);       /* ensure object can be taken at all */
    }

The "actor" parameter refers to which actor is executing the action (usually "Me," the player) and "io" is the indirect object, in this case what you're taking the direct object out of. "Self" here is the scissors. Notice the code says if there is an indirect object ("io <> nil"), and if "self" (the scissors) isn't in it ("isIn" is a special routine which checks this), then it gives you the message we already encountered.

What I need to do is find some way of getting this routine to recognize that an object is plural, and giving the right response for it. My first attempt at a solution involved creating a new property in our scissors object, called "plural," by adding this line somewhere in the scissors definition:

     plural = true

Then I use the "modify" command, which enables me to overwrite individual routines or properties in an object after the object has been defined already, in this case in ADV.T -- though you can't modify individual lines of a routine or property, you have to repeat all of it. Like so:

modify thing

    verDoTakeOut( actor, io ) =
    {
        if ( io <> nil and not self.isIn( io ))
        {
            caps(); self.thedesc; 
            // NEW CODE!
            if ( self.plural ) " aren't"; else " isn't";
            // REST OF OLD CODE
            " in "; io.thedesc; ". ";
        }
        self.verDoTake(actor);       /* ensure object can be taken at all */
    }
;

So I try to compile this and get the message "error TADS-335: invalid vocabulary property value." What does that mean? "Plural = true" is perfectly valid! After some searching, I found out that ADV.T already defines a property called "plural," but as a string. My later code tried to redefine the variable, which is fine, but also tried to redefine its type after it had been defined, from string to boolean true/false. A no-no.

So instead I change "plural" to the less elegant "pluralflag" (though I could've been malicious and used "Plural," since TADS is case-sensitive) and recompile. Now it runs as follows:

     > take scissors from box.
     The scissors aren't in the box.

Now, though, I've learned my lesson. I realize I need to look through ADV.T for further examples of "is" or "isn't" or "goes" or whatever. I'll give you a few more examples, though the full list is much longer.

From thing:
verDoPutOn: "The {direct object} is/are already on the {indirect object}!"
verDoTellAbout: "It doesn't look like the {direct object} is/are interested."

And from actor, another prototype-object, which has routines suitable for your NPCs:

     verDoWalkon: "{Direct object} object/s to that."

So now I've found all of the spots where messages need to be changed, and modified all the objects and routines from ADV.T in my own code to do so. And even better for you, I save my changes as a separate file that other people could incorporate in their games in order to utilize its benefits, right "out of the box," as it were, by just incorporating "pluralflag = true" (or its new name -- see below) in the right objects. Remember, though, not to modify those same routines again in your code after you've included my utility, or you'll replace my changes to ADV.T! A better way if you need to change those same routines would be not to call the utility file from your game separately (technically: #include), but rather to paste my code into your game in its entirety, and fiddle with it there. You'll find it soon in the IF archive under the name plurals.t under Programming/TADS/Examples, or on the enclosed XYZZYnews disk if you've subscribed that way, along with a small test game called pluralstest.t, providing many samples of these objects.

So we're done, right? I thought so, too, until I tried:

     >put scissors
     What do you want to put it in?

Now, this is an error message generated by the parser in response to the lack of an indirect object (grammatically a preposition phrase, but let's not nitpick) when the only existing template or pattern for the verb requires one. You can't just put something, you have to put it in something or on something. The trouble is that pesky "it." With the greater control that version 2.2 gives you over parser error messages, maybe I could modify that as well.

First step: "The New Book of the Parser" in the TADS Version 2.2 Upgrade Manual. Page 90:

"If the verb requires a direct or indirect object, but the command doesn't specify one and the parser can't supply a suitable default...the parser checks to see if your game program defines a function called parseAskobjActor [before going on to its own solution]."
Okay, cool, I'll provide one, but then I find that, even if it's the indirect object you're looking for, the routine doesn't give as a parameter the direct object -- only the actor, the verb, and the preposition, if there is one. Therefore, you have no way of passing to your new parseAskobjActor routine the identity of that direct object and whether it is plural or singular before you ask him or her the question posed above. So, we can't alter that question to be plural-sensitive the way we want it to be. Dead end. I look at the other, more general method that TADS provides to change error messages, the function parseError. It lets you replace any standard error message it generates with a new one, for instance, message number 6, "I expected a noun after 'of'." Again, here we're very limited; we just get the number of the message the parser would've given as the parameter (there's a whole table of them), and can either replace the message in every case the parser would've used it, or let it stand.

The message numbers 140-149 deal with the pieces of the message we could've fiddled with in its entirety with parseAskobjActor, like 140: "What do you want to" or 143: "?". Why you'd want to replace, say, the question mark, I'm not sure, but you can. The parser assembles these pieces around what the player has typed in, for instance "What do you want to " + put + "it" + in + "?".

Number 141 is that pesky "it," but hold on, there's also message number 147, "them." When is that used? Page 91: "If a pronoun for the direct object is required, the parser checks each object in the direct object list. If the player explicitly specified multiple direct objects (by using "all" or a list of noun phrases), the parser displays message 144 ("them")." The text goes on to explain how it uses the properties isHim and isHer of the objects the player specified ("put Aunt Helena," for instance), to possibly substitute the pronouns "him" or "her" as well.

So the only way the parser will use "them" in that cobbled-together message is if you've specified two or more objects ("put the apple and the orange"); another dead end. Since the parser does all the processing without asking for our advice (how rude!), we can't affect it.

My way around this, admittedly an inelegant one, is to replace message 141 with "that." Hey, it makes sense. The way David Baggett's rewrite of ADV.T, called WorldClass, solves the problem is by excluding the pronoun entirely; his parseAskobjActor asks things like "With what?" or "In what?", which is, I must admit, a rather clever if "snappy" response.

A better solution would be to have the parser check my property, which for consistency's sake I now rechristen "isThem," before deciding what pronoun to use. It would be even more ideal if in the next TADS version (if there is a next TADS version) the parser would allow you to replace the whole pronoun-sorting business with your own routine, with the right parameters this time. Flexibility is king. This is where Inform really shines, since all such internal workings of the parser are available for tinkering by the player. (That's why they've already got a full plural-singular utility.)

And that's all the changes needed to implement plural-singulars! There may be others I didn't notice, but in my extensive testing of my own game, I haven't found any (famous last words...). My game has other wholesale changes in ADV.T to make things work more smoothly and logically and to add new features, and I want to make utilities out of those things as well once my game's out. You'll be able to use this, and them, if you don't want to make the wholesale switch to WorldClass. There's also the possibility that TADS itself will include similar improvements in the next version. Michael Roberts, are you listening?

As you can see, I love talking shop, so if anybody has any comments, feel free to e-mail me at the address at the beginning of this article.


[right arrow] Go to the next page in this issue
[left arrow] Flip back to the previous page
[top arrow] Go to the XYZZYnews home page