Action : BasicProd
Note that while all implicit commands are nested, not all nested commands are implicit. A nested command may simply be a component of another command, or another command may be handled entirely by running a different command as a nested command. In any case, a nested but non-implicit command does not appear to the player as a separate command; it is simply part of the original command.
The base Action is intransitive, so it doesn't have any noun-phrase slots, hence this is an empty list.
We announce an object under several circumstances:
- If we are iterating through multiple objects, we'll show the current object to show the player the individual step in the command being performed.
- If 'all' was used to specify the object, we'll announce it even if only one object is involved, to make it clear to the player exactly what we chose as a match.
- If we are executing the command on a single object, and the object was chosen through disambiguation of a set of ambiguous choices, and some of the discarded possibilities were logical but less so than the chosen object, we'll show the assumption we made. In such cases, our assumption is not necessarily correct, so we'll tell the user about our choice explicitly by way of confirmation - this gives the user a better chance of noticing quickly if our assumption was incorrect.
- If we supplied a default for a missing noun phrase in the player's command, we'll show what we chose. Since the player didn't say what they meant, we'll make it plain that we're providing an assumption about what we thought they must have meant.
'info' is the ResolveInfo object describing this resolved object, and 'numberInList' is the total number of objects we're iterating over for this object function (direct object, indirect object, etc). 'whichObj' is one of the object function constants (DirectObject, IndirectObject, etc) describing which object we're mentioning; the language-specific message generator might use this in conjunction with the action to include a preposition with the displayed phrase, for example, or choose an appropriate inflection.
'resolvedAllObjects' indicates where we are in the command processing: this is true if we've already resolved all of the other objects in the command, nil if not. We use this information to get the phrasing right according to the situation.
Each object is only registered once. If a caller attempts to register the same object repeatedly, we'll simply ignore the repeated requests.
This is a convenient way to implement a collective follow-up to the parts of an iterated action. Since repeated registrations are ignored, each handler for an iterated object (such as a "dobjFor" action() handler) can register its follow-up handler without worrying about redundant registration. Then, when the overall action is completed for each iterated object involved, the follow-up handler will be invoked, and it can do any final work for the overall action. For example, the follow-up handler could display a message summarizing the iterated parts of the action; or, it could even scan the transcript for particular messages and replace them with a summary.
actionProp is the custom per-object/per-action property that we normally invoke to process the action. For example, if we're processing verification for the direct object of Take, this would be &verifyDobjTake.
defProp is the default property that corresponds to actionProp. This is the per-object/default-action property that we invoke when the object doesn't provide a "more specialized" version of actionProp - that is, if the object doesn't define or inherit actionProp at a point in its class hierarchy that is more specialized than the point at which it defines defProp, we'll call defProp. If there is a more specialized definition of actionProp for the object, it effectively overrides the default handler, so we do not invoke the default handler.
allProp is the catch-all property corresponding to actionProp. We invoke this property in all cases.
Returns true if there is indeed a Default property that overrides the action, nil if not.
We call verification directly on the object, and we also call verification on the object's preconditions.
If resultSoFar is non-nil, it is a VerifyResultList that has the results so far - this can be used for multi-object verifications to gather all of the verification results for all of the objects into a single result list. If resultSoFar is nil, we'll create a new result list.
Note that this doesn't cause a jump out of the current code, so it's not like 'exit' or the other termination signals. Instead, this simply tells the action to proceed normally for the remainder of the processing for the current object, and then act as though there were no more objects to iterate over, ending the command normally. If you want to cut off the remainder of the execution cycle for the current object, you can use 'exit' (for example) immediately after calling this method.
Intransitive actions don't generally have to do anything in the 'check' phase, since they can simply do any necessary checks in the 'execute' phase that runs immediately after 'check'. This phase is separated out from 'execute' mostly for the benefit of transitive actions, where the 'check' phase gives the involved objects a chance to object to the action.
This should check all of the conditions that must be met for the action to proceed. If any pre-condition can be met by running an implicit command first, that implicit command should be executed here. If any pre-condition cannot be met, this routine should notify the actor and throw an ExitSignal.
Returns true if any implicit commands are executed, nil if not. Implicit commands can only be attempted if allowImplicit is true; if this is nil, a precondition must simply fail (by displaying an appropriate failure report and using 'exit') without attempting an implicit command if its assertion does not hold.
The generic actions defined in the library are always subclassed by language-specific library modules, because the language modules have to define the grammar rules for the verbs - we can't define the grammar rules generically because the verbs wouldn't be reusable for non-English translations if we did. As a result, library code referring to one of the library verbs by name, say TakeAction, doesn't get a language-specific subclass of the verb, but just gets the language-independent base class.
However, to make full use of an Action object in a recursive command, we do need a final language-specific subclass - without this, we won't be able to generate text describing the command, for example. This method bridges this gap by finding a suitable language-specific subclass of the given action, then creating an instance of that subclass rather than an instance of the base class.
By default, we'll take any subclass of this action that is itself a class. However, if any subclass has the property defaultForRecursion set to true, we'll use that class specifically - this lets the language module designate a particular subclass to use as the default for recursive commands, which might be desirable in cases where the language module defines more than one subclass of an action.
Subclasses generally won't override this method, but will instead override the methods that implement the individual steps in the execution sequence.
Intransitive actions must do all of their work in this routine. In most cases, transitive actions will delegate processing to one or more of the objects involved in the command - for example, most single-object commands will call a method in the direct object to carry out the command.
This is the typical way that we disambiguate a list of objects, but this is merely a service routine, so individual actions can choose to use this or other mechanisms as appropriate.
Actions of two or more objects should set this if possible to the resolved object list for the previous noun phrase, as a ResolveInfo list.
The tricky part is that some actions will resolve noun phrases in a different order than they appear in the actual command grammar; similarly, it's also possible that some non-English languages use cataphoric pronouns (i.e., pronouns that refer to noun phrases that appear later in the sentence). To allow for these cases, return an empty list here if a binding is grammatically possible but not yet available because of the resolution order, and note internally that the parser asked us for an anaphoric binding. Afterwards, the action's resolver method must go back and perform *another* resolve pass on the noun phrase production that requested the anaphor binding.
'typ' is the PronounType specifier for the corresponding ordinary pronoun. For 'himself', for example, typ will be PronounHim.
By default, an action has no objects roles at all, so we'll just return an empty list.
For example, if the player typed PUT BOOK AND PENCIL IN BOX, the canonical phrasing we return would be "put (dobj) in (iobj)".
This comes in two forms: if the context indicates we're only attempting the action, we'll return an infinitive phrase ("open the box") for use in a larger participle phrase describing the attempt ("trying to..."). Otherwise, we'll be describing the action as actually having been performed, so we'll return a present participle phrase ("opening the box").
It's important to note that an implied action does NOT count as a nested or replacement action for the purposes of this method. That is, if a command A triggers an implied action B, which triggers a nested action C, and then after that command A itself triggers a nested action D, then
A.getOriginalAction -> A
B.getOriginalAction -> B (B is implied, not nested)
C.getOriginalAction -> C (C is nested within B)
D.getOriginalAction -> A (D is nested within A)
The purpose of the original action is to tell us, mainly for reporting purposes, what we're really trying to do with the action. This allows reports to hide the internal details of how the action is carried out, and instead say what the action was meant to do from the player's perspective.
Language libraries must override this to return the original match tree object if Actions are separate from predicate match trees.
(The 'predicate' is simply the grammar match tree object for the entire verb phrase from the player's actual command entry text that matched this Action. For example, in "BOB, TAKE BOX", the predicate is the match tree for the "TAKE BOX" part. In "BOB, TAKE BOX AND GO NORTH", the predicate for the Take action is still the "TAKE BOX" part. For "BOB, TAKE BOX AND BOOK AND GO NORTH", the predicate for the Take action is "TAKE BOX AND BOOK" - the full verb phrase includes any multiple-object lists in the original command.)
For example, for the verb UNLOCK <dobj> WITH <iobj>, if the unknown is the direct object, the phrase we'd return would be "unlock": this would plug into contexts such as "what do you want to unlock." If the indirect object is the unknown for the same verb, the phrase would be "unlock it with", which would plug in as "what do you want to unlock it with".
Note that we are NOT to include the infinitive complementizer (i.e., "to") as part of the phrase we generate, since the complementizer isn't used in some contexts where the infinitive conjugation is needed (for example, "what should I <infinitive>").
A simple synonym remapping is a remapTo that applies the same verb to a new object in the same role. For example, if we remap OPEN DESK to OPEN DRAWER, then the drawer is the simple synonym remapping for the desk in an OPEN command. A remapping is considered a simple synonym remapping only if we're remapping to the same action, AND the new object is in the same action role as the original object was.
If there's no simple synonym remapping, we'll return nil.
'ctx' is a GetVerbPhraseContext object, which lets us keep track of antecedents when we're stringing together multiple verb phrases. 'ctx' can be nil if the verb phrase is being used in isolation.
Bob, tell me about the planet
("Tell me about..." is a little different from the others. We treat it as conversational because it means the same thing as "ask Bob about...", which we consider conversational because it would be rendered in real life as a question. In other words, the action involves the issuing actor stating the question, which means that issuing actor is the one doing the physical work of the action. "Tell me about..." could be seen as an order, but it seems more appropriate to view it as simply an alternative way of phrasing a question.)
The issuing actor is passed as a parameter because some actions are conversational only in some cases; "tell me about the planet" is conversational, but "tell Bill about the planet" isn't, since the latter doesn't ask Bob a question but orders Bob to talk to Bill.
When the issuing actor and target actor are the same, this is irrelevant. The purpose of this is to distinguish orders given to another character from conversational overtures directed to the other character, so if the command is coming from the player and bound for the player character, there's obviously no conversation going on.
Note also that, contrary to what one might think at first glance, a command like ASK ABOUT is NOT conversational; it's a command to ask someone about something, and isn't itself a conversational overture. The act of asking is itself a conversational overture, but the asking is the *result* of the command, not the command itself. An action is only conversational if the action itself is a conversational overture. So, "BOB, HELLO" is conversational; "BOB, ASK BILL ABOUT COMPUTER" is not, because it orders Bob to do something.
We'll announce the object only if it's marked as defaulted or unclearly disambiguated, and then only if the other list will be announcing its objects as multi-action objects. However, we do not pre-announce anything for a remapped action, because we'll show the full action description for each individually announced object, so we don't need or want a separate announcement for the group.
Returns true if we did any pre-announcing, nil otherwise. If we return true, the caller should not re-announce this object during the iteration, since our pre-announcement is common to all iterations.
This approach ('grammar predicate' matches are based on Action subclasses) works well for languages like English that encode the role of each phrase in the word order of the sentence.
Languages that encode phrase roles using case markers or other devices tend to be freer with word order. As a result, 'predicate' grammars for such languages should generally not attempt to capture all possible word orderings for a given action, but should instead take the complementary approach of capturing the possible overall sentence structures independently of verb phrases, and plug in a verb phrase as a component, just like noun phrases plug into the English grammar. In these cases, the match objects will NOT be Action subclasses; the Action objects will instead be buried down deeper in the match tree. Hence, resolveAction() must be defined on whatever class is used to construct 'predicate' grammar matches, instead of on Action, since Action will not be a 'predicate' match.
This routine is meant for use only for actions built programmatically using setResolvedObjects(). In particular, we assume that we have only one object in each slot. For normal parser-built actions, this check isn't necessary, because the parser only resolves objects that are in scope in the first place.
The arguments to this routine can either be match tree objects, which we'll plug into our grammar tree in the respective roles exactly as given; or they can be ResolveInfo objects giving the desired resolutions, in which case we'll build the appropriate kind of PreResolvedProd for each one. The types can be freely mixed.
An action has an original action if it's a nested or replacement action for an action.
One example of where this is useful is in global action remapping cases where the target actor changes in the course of the remapping. For example, if we remap BOB, GIVE ME YOUR BOOK to ASK BOB FOR YOUR BOOK, the YOUR qualifier should still refer to Bob even though the command is no longer addressing Bob directly. This routine can be used in this case to override the meaning of 'you' so that it refers to Bob.
This method is used to set up an action to be performed programmatically, rather than based on parsed input. Since there's no parsed input in such cases, the objects are specified directly by the programmatic caller.
Each entry in objList is an object involved in the action. For each entry in objList, there must be *THREE* entries in propList: a verify property, a check property, and an action property. If any of these three properties is defined on the corresponding object, we'll allow the command to proceed. If we can find none of the given handler properties on any of our objects, we'll add an "illogical" verify result.