VocabObject : object
In most cases, the prompt order doesn't matter, so most objects won't have to override the default setting. Sometimes, though, a set of objects will be identified in the game as "first", "second", "third", etc., and in these cases it's desirable to have the objects presented in the same order as the names indicate:
Which door do you mean, the first door, the second door, or the third door?
By default, we use the same value as our pluralOrder, since the plural order has essentially the same purpose.
In most cases, the plural resolution order doesn't matter. Once in a while, though, a set of objects will be named as "first," "second," "third," and so on; in these cases, it's nice to have the order of resolution match the nominal ordering.
Note that the sorting order only applies within the matches for a particular plural phrase, not globally throughout the entire list of objects in a command. For example, if the player types TAKE BOXES AND BOOKS, we'll sort the boxes in one group, and then we'll sort the books as a separate group - but all of the boxes will come before any of the books, regardless of the plural orders.
>take books and boxes
first book: Taken.
second book: Taken.
third book: Taken.
left box: Taken.
middle box: Taken.
right box: Taken.
'adj adj adj noun/noun/noun*plural plural plural'
The noun part of the string can be a hyphen, '-', in which case it means that the string doesn't specify a noun or plural at all. This can be useful when nouns and plurals are all inherited from base classes, and only adjectives are to be specified. (In fact, any word that consists of a single hyphen will be ignored, but this is generally only useful for the adjective-only case.)
During preinitialization, we'll parse this string and generate dictionary entries and individual vocabulary properties for the parts of speech we find.
Note that the format described above is specific to the English version of the library. Non-English versions will probably want to use different formats to conveniently encode appropriate language-specific information in the initializer string. See the comments for initializeVocabWith() for more details.
You can use the special wildcard # to match any numeric adjective. This only works as a wildcard when it stands alone, so a string like "7#" is matched as that literal string, not as a wildcard. If you want to use a pound sign as a literal adjective, just put it in double quotes.
You can use the special wildcard "\u0001" (include the double quotes within the string) to match any literal adjective. This is the literal adjective equivalent of the pound sign. We use this funny character value because it is unlikely ever to be interesting in user input.
If you want to match any string for a noun and/or adjective, you can't do it with this property. Instead, just add the property value noun='*' to the object.
The purpose of weak tokens is to allow players to use more words to refer to some objects without creating ambiguity. For example, if we have a house, and a front door of the house, we might want to allow the player to call the front door "front door of house." If we just defined the door's vocabulary thus, though, we'd create ambiguity if the player tried to refer to "house," even though this obviously doesn't create any ambiguity to a human reader. Weak tokens fix the problem: we define "house" as a weak token for the front door, which allows the player to refer to the front door as "front door of house", but prevents the front door from matching just "house".
By default, this is nil to indicate that we don't have any weak tokens to check. If the object has weak tokens, this should be set to a list of strings giving the weak tokens.
This routine is called whenever the parser is called upon to resolve a pronoun ("TAKE THEM"). This routine is called for each object in the "raw" pronoun binding, which is simply the list of objects that was stored by the previous command as the antecedent for the pronoun. After this routine has been called for each object in the raw pronoun binding, the final list will be passed through filterResolveList().
'lst' is the raw pronoun binding so far, which might reflect changes made by this method called on previous objects in the list. 'typ' is the pronoun type (PronounIt, PronounThem, etc) describing the pronoun phrase being resolved. The return value is the new pronoun binding list; if this routine doesn't need to make any changes, it should simply return 'lst'.
In some cases, filterResolveList chooses which of two or more possible ways to bind a noun phrase, with the binding dependent upon other conditions, such as the current action. In these cases, it's often desirable for a subsequent pronoun reference to make the same decision again, choosing from the full set of possible bindings. This routine facilitates that by letting the object put back objects that were filtered out, so that the filtering can once again run on the full set of possible bindings for the pronoun reference.
This base implementation just returns the original list unchanged. See CollectiveGroup for an override that uses this.
'lst' is a list of ResolveInfo objects describing the tentative resolution of the noun phrase. 'action' is the Action object representing the command. 'whichObj' is the object role identifier of the object being resolved (DirectObject, IndirectObject, etc). 'np' is the noun phrase production that we're resolving; this is usually a subclass of NounPhraseProd. 'requiredNum' is the number of objects required, when an exact quantity is specified; this is nil in cases where the quantity is unspecified, as in 'all' or an unquantified plural ("take coins").
The result is a new list of ResolveInfo objects, which need not contain any of the objects of the original list, and can add new objects not in the original list, as desired.
By default, we simply return the original list.
The parser uses an object's facets to resolve a pronoun when the original antecedent goes out of scope. In our door example, if we refer to the door, then walk through it to the other side, then refer to 'it', the parser will realize from the facet relationship that 'it' now refers to the other side of the door.
By default, we'll simply return our 'owner' property.
Since a class can be inherited more than once in an inheritance tree (for example, a class can have multiple superclasses, each of which have a common base class), we keep a vector of all of the classes we've visited. If we're already in the vector, we'll skip adding vocabulary for this class or its superclasses, since we must have already traversed this branch of the tree from another subclass.
Note that this parsing is intentionally located in the English-specific part of the library, because it is expected that other languages will want to define their own vocabulary initialization string formats. For example, a language with gendered nouns might want to use gendered articles in the initializer string as an author-friendly way of defining noun gender; languages with inflected (declined) nouns and/or adjectives might want to encode inflected forms in the initializer. Non-English language implementations are free to completely redefine the format - there's no need to follow the conventions of the English format in other languages where different formats would be more convenient.
This method returns true if 'self' is owned by 'obj'. The parser generally tests for ownership in this direction, as opposed to asking for obj's owner, because a given object might have multiple owners, and might not be able to enumerate them all (or, at least, might not be able to enumerate them efficiently). It's usually efficient to determine whether a given object qualifies as an owner, and from the parser's persepctive that's the question anyway, since it wants to determine if the "x" in "x's y" qualifies as my owner.
By default, we simply return true if 'obj' matches our 'owner' property (and is not nil).
- this object is in scope;
- our vocabulary matches the noun phrase, which means that ALL of the words in the player's noun phrase are associated with this object with the corresponding parts of speech. Note the special wildcard vocabulary words: '#' as an adjective matches any number used as an adjective; '*' as a noun matches any word used as any part of speech.
'origTokens' is the list of the original input words making up the noun phrase, in canonical tokenizer format. Each element of this list is a sublist representing one token.
'adjustedTokens' is the "adjusted" token list, which provides more information on how the parser is analyzing the phrase but may not contain the exact original tokens of the command. In the adjusted list, the tokens are represented by pairs of values in the list: the first value of each pair is a string giving the adjusted token text, and the second value of the pair is a property ID giving the part of speech of the parser's interpretation of the phrase. For example, if the noun phrase is "red book", the list might look like ['red', &adjective, 'book', &noun].
The adjusted token list in some cases contains different tokens than the original input. For example, when the command contains a spelled-out number, the parser will translate the spelled-out number to a numeral format and provide the numeral string in the adjusted token list: 'a hundred and thirty-four' will become '134' in the adjusted token list.
If this object does not match the noun phrase, this routine returns nil. If the object is a match, the routine returns 'self'. The routine can also return a different object, or even a list of objects - in this case, the parser will consider the noun phrase to have matched the returned object or objects rather than this original match.
Note that it isn't necessary to check that the input tokens match our defined vocabulary words, because the parser will already have done that for us. This routine is only called after the parser has already determined that all of the noun phrase's words match ours.
By default, we do two things. First, we check to see if ALL of our tokens are "weak" tokens, and if they are, we indicate that we do NOT match the phrase. Second, if we pass the "weak token" test, we'll invoke the common handling in matchNameCommon(), and return the result.
In most cases, games will want to override matchNameCommon() instead of this routine. matchNameCommon() is the common handler for both normal and disambiguation-reply matching, so overriding that one routine will take care of both kinds of matching. Games will only need to override matchName() separately in cases where they need to differentiate normal matching and disambiguation matching.
In most cases, when a game wishes to customize name matching for an object, it can simply override this routine. This routine provides common handling for matchName() and matchNameDisambig(), so overriding this routine will take care of both the normal and disambiguation matching cases. In cases where a game needs to customize only normal matching or only disambiguation matching, it can override one of those other routines instead.
This routine is separate from matchName() because a disambiguation response usually only contains a partial name. For example, the exchange might go something like this:
Which box do you mean, the cardboard box, or the wood box? >cardboard
Note that it is not safe to assume that the disambiguation response can be prepended to the original noun phrase to make a complete noun phrase; if this were safe, we'd simply concatenate the two strings and call matchName(). This would work for the example above, since we'd get "cardboard box" as the new noun phrase, but it wouldn't work in general. Consider these examples:
>open post office box
Which post office box do you mean, box 100, box 101, or box 102?
Which jewel do you mean, the emerald, or the diamond?
There's no general way of assembling the disambiguation response and the original noun phrase together into a new noun phrase, so rather than trying to use matchName() for both purposes, we simply use a separate routine to match the disambiguation name.
Note that, when this routine is called, this object will have been previously matched with matchName(), so there is no question that this object matches the original noun phrase. The only question is whether or not this object matches the response to the "which one do you mean" question.
The return value has the same meaning as for matchName().
By default, we simply invoke the common handler. Note that games will usually want to override matchNameCommon() instead of this routine, since matchNameCommon() provides common handling for the main match and disambiguation match cases. Games should only override this routine when they need to do something different for normal vs disambiguation matching.