Controlling text with the text processor - ThePix/QuestJS Wiki

These directives change the content of the text depending on the state of the game world. Probably the most important use is to make descriptions dynamic. The dog might be yapping one time, or chasing his tail another time or sniffing the strange object. The warning light might be yellow or red or off. There might be a hole in the wall after the explosion.

Most of them will take the name of an object as a parameter, but you can also use "player", "currentLocation" or "settings" to represent the player object, the current location or the settings object (as you might have guessed), whatever their actual names. Using parameters is discussed more elsewhere.

The directives are divided up into hiw many texts are included in the directive itself. For example, the if directive has two, one for when the condition is true, one for when if is false (though the latter is optional). Thus we have directives with zero, two or more text option.

You need to be a little bit careful with spaces with these and other directives that are shown conditionally. You need to consider what spaces will be required in either situation. Usually it is best to have a space inside the text directive, at the start of the text, as indicated below.

The room is a mess.{once: That is a surprise!} Clothing is strewn all around.
                         ^

If the text is used, there will be a space before it and after it. If the text is not used, there will still be a single space between the two sentences. This example has the extra text as a fragment within a sentence. The comma needs to be inside the text directive.

The room is a mess{once:, which is a surprise}. Clothing is strewn all around.

You can add a further option will be used if the first string is not.

The room looks incredible! {once:It blows your mind:Been there, done that}.

Zero text options

show

To show an attribute, use show. You then need the object, then the attribute name, all separated by colons.

You have {show:player:hitpoints} hit points.

You can actually omit the "show", and for compatibility with Quest 5, if "show" is omitted, you can use a dot to separate the object name and attribute name, however, some new features may not work so if you are creating a string from scratch use "show". Nevertheless, these will give the same result as the above:

You have {player:hitpoints} hit points.
You have {player.hitpoints} hit points.

If the attribute is a function, it should return a string; the function will be called, with the text processor parameters, and the resultant string inserted into the text. This contrived example is from the unit tests.

  player.tpTest1 = function(params) { return lang.toWords(2 * params.val) }
  processText("Simple text: {show:player:tpTest1}", {val:8})

-> "Simple text: sixteen"

If the attribute does not exist or is set to null, undefined or false nothing will be shown.

number and ordinal

Use these to display a number in words, rather than digits.

You have {number:player:hitpoints} hit point{ifMoreThan:player:hitpoints:0:s}.

Note that show can also be used to display parameter values, as discussed here. It will try to match an object in the parameters first, before looking for one in the game world.

money

Use the money directive to display money, using the format set up in settings.js. This can be used with a number (it assume a whole number) or with the name of an object. If an object, the object's selling price is used if held by the player, or the buying price otherwise (see the MERCH template), if these are set. If not available, the price of the object is used (if no price is set you will get an error message). If the object is the player, the player's "money" attribute is used.

You can use a dollar sign as a short cut for this directive.

The carrot is {money:carrot}
The carrot is {$:carrot}
You see {$:12}

dateTime

Use dateTime to display the current in-game date and time. The format is set in settings.

exits and objects and rooms

There are a number of directives that are designed to be used with the room template. This is how Quest decides the format for a room description. This is the default, an array of four strings, giving four paragraphs in the description.

  roomTemplate:[
    "#{cap:{hereName}}",
    "{terse:{hereDesc}}",
    "{objectsHere:You can see {objects} here.}",
    "{exitsHere:You can go {exits}.}",
  ],

The first line uses hereName to get the name of the current room (with "the" added if not flagged as a proper name). The cap directive makes it start with a capital letter. The # at the start of the line makes it into a heading (and is not actually a feature of the text processor).

The second line is the room description, using hereDesc, together with terse so the description is omitted in TERSE mode.

The third line lists the items here. The objectsHere directive means that if no objects are here this will not appear. The fourth line does the same for exits.

This offers a lot of flexibility. Here is an alternative version that puts everything in a single paragraph.

  roomTemplate:[
    "You are in {hereName}. {terse:{hereDesc}} {objectsHere:You can see {objects} here.} {exitsHere:You can go {exits}.}",
  ],

Two text options

All of these check a condition; if true it uses the first text, if false it uses the second, though the second is optional in every case.

once and notOnce

Text with the "once" directive will only be seen once, the first time the text is displayed. Conversely, if "notOnce" is used, the text is only seen after the first time.

The room is a mess.{once: That is a surprise!} Clothing is strewn all around.

You can use notfirst as synonym, to ease conversion from Quest 5. You might also want to look at the various functions that fire when a room is entered, described here. For text that is just seen once, but could be in any of several descriptions, consider a custom directive, as shown here.

It is bad practice to put anything important in text that will only be seen once, as users can miss it, and then have no opportunity to find it again.

if and ifNot

You can also use the Quest 5 format for all if directives, to ease conversion.

These will test the value of an attribute. It needs the object name, then the attribute name.

If the attribute is a Boolean (and if it does not exist, in which case it will be taken to have the value false) it then needs the string to show if true, then, optionally, the string to show it false.

Player has that odd thing: {if:player:someOddAtt:yes:no}

If the attribute is an integer or string, it needs the value to test against, before the values to show.

Is the player exactly forty? {if:player:age:40:yes:no}

The ifNot directive is the same, but works in reverse.

If the attribute is a function, the function will be called, with the text processor parameters, and the resultant value used for comparison (if the result is an object, its name will be used). This contrived example is from the unit tests.

  player.tpTest2 = function(params) { return 2 * params.val }
  "Simple text: {if:player:tpTest2:16:yes:no}", {val:8})

-> "Simple text: yes"

NOTE: When using with numbers and strings, you need to be certain these attributes exist. If you are thinking the attribute should be a number or string, but in fact it does not exist, Quest will assume if is a Boolean, and take the false value, which will almost certainly be wrong. In the above example, if there is no player.age attribute, Quest will output "Is the player exactly forty? yes". See next section!

ifIs and ifNotIs

Like "if" and "ifNot", but Quest will try to guess the type based on the content of the text directive, rather than the attribute on the object. This can be helpful if it might not exist. Looking at the player age example again:

Is the player exactly forty? {ifIs:player:age:40:yes:no}

In this case, Quest will see the parameter is 40, and will assume we are interested in a number. It will then look at player.age, if that is 40, we get "yes", otherwise we get "no", even if player.age does not exist.

For Boolean attributes, you need to specify its value, unlike with "if" and "ifNot":

Player has that odd thing: {if:player:someOddAtt:true:yes:no}

If you check if a Boolean attribute is true, then if the attribute has any other value, or does not exist, this will say "no", as you would expect. If you are checking for false, then any other value will say "no", so in this case no attribute will behave the same as the attribute being true.

ifExists and ifNotExists

Like "if" and "ifNot", but they test if the attribute exists on the given object, rather than testing what it is.

ifPlayer and ifNotPlayer

Like "if" and "ifNot", but they test if the given object is the player.

... {ifPlayer:char:That strikes you as odd. }...

ifLessThan, ifMoreThan, ifLessThanOrEqual, ifMoreThanOrEqual

These are similar to "if" and "ifNot", but can only be used with integer values.

Is the player forty or over? {ifMoreThan:player:age:39:yes:no}

See also the example in "number".

ifHere, ifNotHere, ifHeld, ifNotHeld

These are also similar to "if" and "ifNot", but depend on whether the named object is here (in the room) or held, i.e., isAtLoc returns true for player.loc or player.name respectively.

This is a messy room.{ifHere:puddle: There is a puddle of water on the floor.}

The Quest 5 format is also supported for "here".

Multiple text options

These allow any number of texts in the directive.

random

The random directive will show one of the following options, picked at random:

There is a small dog here. {random:It is chasing its tail:It is yapping at nothing:It is looking at you strangely}.

As with Quest 5, The text processor is invoked when text is printed to screen and this means random text will be randomised every time it is printed. That is good if you want some random event to add interest, but not so good if the ball has a random colour each time the player looks at it. In the latter situation, you should invoke the text processor when the item is created, with the processText function. This example shows how you could set the examine attribute of a ball:

createItem("ball", TAKEABLE(), {
  loc:"dining_room",
  examine:processText("The ball is {random:blue:red:green}."),
}

select

You can use "select" to pick from a list in the directive itself - kind of like "random", but using an attribute to select a specific value. In this example, the "colour" attribute of the item is used to pick a value. In this case green will get displayed.

  w.Kyle.colour = 1
  msg("Kyle is {select:Kyle:colour:red:green:blue}.")

Note that it counts from zero!

To aid conversion from Quest 5, you can also do "Kyle is {select:Kyle.colour:red:green:blue}.".

Alternatively, you can select from a list. In this example, which is effectively the same as above, the object "Kyle" is given two attributes, one for the list and one for the value. Changing the value of Kyle.colour will cause the message to change when next printed.

  w.Kyle.colours = ['red', 'green', 'blue']
  w.Kyle.colour = 1
  msg("Kyle is {select:Kyle:colours:colour}.")

Note that both attributes must be on the same object.

This version is probably better for longer descriptions, and for where the same list is used in different places. This example shows how you can have an object's description change when its state changes.

createItem("robot" {
  loc:"lab",
  state:0,
  descs:[
    "A bunch of junk... but perhaps you can make something from it?",
    "It looks ugly, but it is a working robot.",
    "The robot's eyes are glowing red - never a good sign.",
    "A pile of junk that used to be a robot.",
  ],
  examine:"{select:robot:descs:state}",
}

There are actually four variations that you can use to determine how Quest deals with values out of range.

Name Effect
selectNone Nothing (same as the default)
selectWrap Repeated cycles through the list
selectStart Returns the first string
selectEnd Returns the last string

roomSet

You can create a set of rooms using the ROOM_SET template. This directive will then print text according to the order the rooms in the set are entered.

This example is for the "desc" attribute of a location in a set. If this room is he first of the set to be entered, the user will see the first text every time the room is entered. If this is the second to be entered, the user will see the second, etc. In this case, if this is the fourth or later room, no text will be seen from the text processor.

{roomSet:When you enter the corridor, you are blown away by the amazing ceiling:This part of the corridor is like the other:More of he same corridor}. It continues south, and there is a door north.