Customize Verbs:Anatomy of a Verb
Adventurejs's verbs fall into a small number of patterns. There are direction verbs, which are fairly simple and quickly punt to a global tryTravel() method. The bulk of verbs have to do with physical manipulation. (Well, "physical" manipulation. Since of course it's all virtual.) For example, lock, unlock, hit, throw, and write are just a few common physical manipulation verbs. Most of these verbs' code structures will look something like the diagram below. The diagram doesn't represent any particular verb, as every verb has its own quirks. Some of the blocks shown below may exist in some verbs but not others, and some of the function calls may or may not be used by any verb. But hopefully this will offer some idea of how a verb is constructed.
-
@augments {adventurejs.Verb} This defines the verb as an instance of adventurejs.Verb. All verbs are Verb instances.
-
@class this_verb_name
All verbs are instances, not classes, but we use this native JSDoc tag to print this verb's name to the doc page.
-
@ajsnode game.dictionary.verbs.this_verb_name
The dot notation path to the verb at runtime. Provided chiefly for reference, in case authors should want to reference the verb from custom code.
-
@ajsconstructedby adventurejs.Dictionary#createVerb
This shows the Adventurejs method that is used to construct a verb.
-
@ajsconstruct MyGame.createVerb({ "name": "this_verb_name", [...] });
This shows how to construct a new verb in Adventurejs. Authors shouldn't ever have to construct the built-in verbs, but this can be applied to new verbs.
-
@hideconstructor
This is a value used by JSDoc when generating documents. We use it to tell JSDoc generation to exclude its native constructor block, because we replace that with custom blocks.
-
@ajsinstanceof Verb
This is a custom field used by Adventurejs to generate a custom constructor block to the doc.
-
@ajsnavheading SomeNavHeading
This is a custom field used by Adventurejs to set which navbar heading to file this item under.
-
@summary
A brief one sentence summary of the verb. This is a native JSDoc field that appears in a couple of places.
-
@ajssynonyms this_verb_name
This is a custom Adventurejs field that is used to document synonyms for this verb. Synonyms allow users to input alternate names for the verb. For example, verb "break" has synonyms "smash" and "destroy".
-
@tutorial VerbBasics
This is a native JSDoc field that creates a link to a tutorial page, using JSDoc's native link building function. Multiple @tutorial tags can be included per item.
-
@classdesc
This is a native JSDoc field used to generate a description of the class (or in the case of verbs, the instance).
-
@ajsverbactions tryVerb, tryVerbThis, tryVerbThisWithThat, tryVerbThatWithThis, doVerb, doVerbThis, doVerbThisWithThat, doVerbThatWithThis
This is a custom Adventurejs field used to note which verb actions this verb attempts to call.
-
@ajsverbphases doBeforeTry, doAfterTry, doBeforeSuccess, doAfterSuccess
This is a custom Adventurejs field used to note which verb phases this verb attempts to call. Most verbs support all these. A few, such as again and undo support none.
JSDoc comment block
These tags are used by JSDoc to generate documentation for Adventurejs. Adventurejs uses a heavily customized version of the default JSDoc template. Some tags have been repurposed from the original JSDoc intent, and some are unique to Adventurejs. Any tag that starts with @ajs is an Adventurejs tag.
- String properties: name, prettyname, state, past_tense, etc
- accepts_structures
accepts_structures is used to help the parser exclude input that the verb can't handle, before it reaches the verb. For example if the verb does not take an indirect object and only accepts a sentence structure of verb noun, the parser will block input that refers to an indirect object.
- phrase1
Phrases work with structures to help the parser exclude input that the verb can't handle, before it reaches the verb. A verb that doesn't handle an indirect object will only have one phrase. Phrases can help the parser determine whether the verb accepts or requires a preposition, what that preposition must be, and what conditions must be true for any noun that the verb refers to.
- phrase2
Phrases work with structures to help the parser exclude input that the verb can't handle, before it reaches the verb. A verb that can handle an indirect object will generally have at least two phrases. Phrases can help the parser determine whether the verb accepts or requires a preposition, what that preposition must be, and what conditions must be true for any noun that the verb refers to.
- phrase3
Phrases work with structures to help the parser exclude input that the verb can't handle, before it reaches the verb. The majority of verbs have only one or two phrases. Some verbs may have three. For example, attach pipe to flange with wrench would take three phrases, one for each noun in the input. Phrases can help the parser determine whether the verb accepts or requires a preposition, what that preposition must be, and what conditions must be true for any noun that the verb refers to.
- with_params
with_params can contain optional parameters that are specific to the verb. Currently it's most commonly being used to set connections and max_connections, which allow assets to be connected by the verb. For example, such as how plugIn connects the asset being plugged in to the asset it is plugged in to.
- Declare vars
Each turn in Adventurejs is stored in a global input object, so that all functions have access to it. The reason why we break verbs into multiple phases is to allow authors to inject code at various points. The tradeoff is that we need to redeclare our local vars for each block. In some cases we may create local vars in doTry that we want to carry forward to doSuccess: in those cases, we can save variables to input.verb_params.my_param.
- direct_object.callAction(tryVerb)
Verb actions are tied to specific actions. They exist to provide authors with more specific hooks, to preempt logic that runs during the doTry and doSuccess phases. In this example, the verb checks the direct object's verb subscription for onTryVerb. "onTryVerb" is a placeholder here - for example if we used the verb lock, the verb would check the direct object's verb subscription for onTryLock. See Verb Actions to learn more.
- direct_object.isDOV()
Check whether the direct object is subscribed to this verb.
- verb.hasState() && direct_object.isState()
Check whether the verb has a verb state and if the object's is set to that state.
- direct_object.DOVallowOnce() && direct_object.DOVdidDo()
Check whether the direct object's verb subscription may only be applied once, and if it has already been applied.
- direct_object.DOVallowWithNothing() ?
Check whether the direct object's verb subscription allows the verb to be applied without an indirect object.
- direct_object.DOVhasIndirectObjects() ?
If the direct object's verb subscription does not allow the verb to be applied without an indirect object, are any indirect objects defined?
- tryToInferIndirectObject()
If an indirect object is required but has not been supplied, check whether the player is carrying an indirect object that can be inferred. This may be subject to game settings regarding the automatic use of verbs.
- direct_object.callAction(tryVerbThisWithThat)
Verb actions are tied to specific actions. They exist to provide authors with more specific hooks, to preempt logic that runs during the doTry and doSuccess phases. In this example, the verb checks the direct object's verb subscription for onTryVerbThisWithThat. "onTryVerbThisWithThat" is a placeholder here - for example if we used the verb lock, the verb would check the direct object's verb subscription for onTryLockThisWithThat. See Verb Actions to learn more.
- indirect_object.callAction(tryVerbThatWithThis)
Verb actions are tied to specific actions. They exist to provide authors with more specific hooks, to preempt logic that runs during the doTry and doSuccess phases. In cases where we have a direct object and an indirect object, we check both objects for verb actions. The point of this is to provide maximum flexibility to authors. So for example, an author can hook into a verb action on either the direct object or the indirect object, as they prefer. See Verb Actions to learn more.
- direct_object.DOVallowWithAnything()
An indirect object has been provided, now check whether this direct object's verb subscription allows the verb to be applied with any indirect object.
- direct_object.DOVallowWithNothing()
An indirect object has been provided, but check whether this direct object's verb subscription allows the verb to be applied without an indirect object.
- direct_object.DOVallowWithAsset(indirect_object)
An indirect object has been provided, so check whether this direct object's verb subscription allows the verb to be applied with this particular indirect object.
- indirect_object.IOVallowOnce() && indirect_object.IOVdidDo()
Check if this indirect object's verb subscription only allows one use and if it has already been used.
- handleFailure()
handleFailure is inherited from Verb and are never modified in a verb instance. It handles a bit of additional logic that is universal to most verb failures, which includes looking for custom failure messages and sending the combined results to print. Though handleFailure is shown in this diagram as the last block in doTry, it may be called from any block that halts the verb, and typically can be found in multiple places. To see more of what handleFailure does, see the Verb Process reference.
- Declare vars
Each turn in Adventurejs is stored in a global input object, so that all functions have access to it. The reason why we break verbs into multiple phases is to allow authors to inject code at various points. The tradeoff is that we need to redeclare our local vars for each block. In some cases we may create local vars in doTry that we want to carry forward to doSuccess: in those cases, we can save variables to input.verb_params.my_param.
- direct_object.callAction(doVerbThis)
Verb actions are tied to specific actions. They exist to provide authors with more specific hooks, to preempt logic that runs during the doTry and doSuccess phases. In this example, the verb checks the direct object's verb subscription for onDoVerb. "onDoVerb" is a placeholder here - for example if we used the verb lock, the verb would check the direct object's verb subscription for onDoLock. See Verb Actions to learn more.
- direct_object.callAction(doVerbThisWithThat)
Verb actions are tied to specific actions. They exist to provide authors with more specific hooks, to preempt logic that runs during the doTry and doSuccess phases. In cases where we have a direct object and an indirect object, we check both objects for verb actions. The point of this is to provide maximum flexibility to authors. So for example, an author can hook into a verb action on either the direct object or the indirect object, as they prefer. See Verb Actions to learn more.
- direct_object.callAction(doVerbThatWithThis)
Verb actions are tied to specific actions. They exist to provide authors with more specific hooks, to preempt logic that runs during the doTry and doSuccess phases. In cases where we have a direct object and an indirect object, we check both objects for verb actions. The point of this is to provide maximum flexibility to authors. So for example, an author can hook into a verb action on either the direct object or the indirect object, as they prefer. See Verb Actions to learn more.
- Apply state changes
Some verbs may apply state to the assets they act upon. For instance, the verb lock may set asset.is.locked on appropriate assets.
- Compose output
Output is composed contextually to include references as needed to direct and indirect objects and appropriate prepositions.
- handleSuccess()
handleSuccess is inherited from Verb and is never modified in a verb instance. It performs several other actions that are common to all verbs. It is typically the last call made from doSuccess, but some verbs, such as examine, may have several different success blocks depending on how they've been interpreted, with handleSuccess appearing in each block. To see more of what handleSuccess() does, see the Verb Process tutorial.