-
Notifications
You must be signed in to change notification settings - Fork 2
QuestML Language Reference
QuestML is the language which is used to create StoryQuest stories. The author has to provide the story's text in QuestML format. QuestML is based on Markdown, which is a lightweight format for adding markup to plain text. QuestML adds some simple commands in a common format to Markdown to enable authors to interact with StoryQuest's internal services.
A "station" or "station node" is a single node in the story tree. A station ususally has some text attached. Think of a station as the "chapter" in a common adventure book. As this, a station has some metadata associated with it: the station id is equivalent to the chapter number (and is also used in "links") and possibly other data like style of text display, colors, fonts, and other data.
Links are the "binding elements" that lead from one station to other stations. In an adventure book, the player will "jump" to other chapters at the end of one chapter. These "jumps" are conditional, either by choice of the player or by other conditions, like "you can use this path if you have the sword". This concept is also present in StoryQuest and QuestML in the form of "links".
QuestML is basically a text only format. Authors write their story in plain text. They can use some markup to style text. For example, to style a text in italics, enclose it with "*". More on the possible markup below. In addition to the text markup (which follows the "Markdown" concept), QuestML also contains "statements" that can be used to tap into the internals of the StoryQuest system. With statements, you can "remember" choices, remember values or create advanced formatting for text. It is also possible to do advanced scripting, like getting a random value inside the text or even let the player do some input (for example, a character's name) and refer to it later in the text. With statements, it is possible to let the player enter his name and all people in the book can directly address the player by his name. It is also possible to play sounds or video from statements and, for example, create a dynamic weather system that plays rainfall sounds when it is applicable.
Variables are named values that can change. With variables, a story can remember values like the name of the player, or the numeric value of a sword.
Flags are special variables, that can only hold a boolean value: "true" or "false". So a flag can either be "set" or "not set". Authors can use flags as a shorthand to remember values like "the player has already visited this place".
QuestML contains some lightweight markup to enable light styling on the text. The used markup is based on Markdown and most of the syntax can be used inside of a QuestML document. The following section only describes the most commonly markup used in StoryQuest books. Please refer to the Markdown syntax guide for more details.
Headlines can be created with lines starting with "#". The count of "#" at the beginning of the line determines the level of headline:
## This would be a second level headline
Headlines are converted into HTML while displaying the chapter in the StoryQuest app. Styling can be achieved by creating rules in the CSS styling document inside the StoryQuest project.
Emphasis highlights a phrase inside a paragraph. In the default styling, emphasised text would appear in italics. To emphasis a phrase, enclose it with "*":
In this sentence, *this part* would be in italics.
As ususal, the actual styling can be changed in the CSS document.
Strong text works the same as emphasised text, but with a different styling in the output. In the default styling, strong text would be displayed in bold font and must be enclosed with "**":
In this sentence, **this part** would be in bold.
As ususal, the actual styling can be changed in the CSS document.
The following describes all currently available statement types. Some of the statements include expressions in parameters. These expressions can be described in JavaScript syntax with one important difference: parameters are enclosed in [
and ]
instead of (
and )
. This only applies to expressions in parameters. Expressions in body text (for example in the script
statement) can utilize standard JavaScript.
Links provide a way to link from one station to another station. They represent one of the basic building blocks for branching text. To add a link to the text, add the following statement:
{link(targetStationId, flagName, isEnabled):choiceText}
with the following parameters:
-
targetStationId
: the id of the station node to be linked to. -
flagName
(optional): if this is set, the flag namedflagName
is set to true when the player touches the button (in addition to switching to the new station node). -
isEnabled
(optional): this can be "true", "false", a flag name or an expression (see above). If the value is "true" or a given flag name is evaluated to "true" (either the flag being set or a value with the same name is set to something not empty), the link is active. Otherwise the link is visible but disabled. If not given, the link is active. -
choiceText
: the text displayed on the choice button.
NOTE: the link conditions isEnabled
is live checked and can be any javascript expression or a name of a variable. If the condition evaluation result or the contents of the model variable changes, the link state also changes (subject to a delay of the check loop of ~1 second).
{link(the_temple, hasVisitedTheTemple, hasTheChalice):
You have the chalice, you can enter the temple.}
If the flag hasTheChalice
is true, the choice is active and can be touched. When a player chooses this link, the flag hasVisitedTheTemple
will be set to true
and the book jumps to the station the_temple
. The button text is You have the chalice, you can enter the temple.
.
{link(deeper_into_the_woods):Continue your path}
This is a simple link with no additional parameters, it simply displays a link button Continue your path
which jumps to station deeper_into_the_woods
.
Links and Dice Dropins: sometimes, it is required that a dice is rolled before a choice can be made. StoryQuest includes a service method localDiceRolled
to determine whether a dice on the same page has rolled. The function returns true if one or more dice on the same page has been rolled. This can be used to activate a link when a dice has been rolled:
{link(the_temple, hasVisitedTheTemple, hasTheChalice&&localDiceRolled[]):
You have the chalice, you can enter the temple.}
Links are always displayed as blocks and are treated as a seperate paragraph. To create inline links that are embedded into flowing text, the ilink
statement can be used:
{ilink(targetStationId, flagName, isEnabled):choiceText}
The ilink
has the same parameters as the link
statement and the same functionality. The only difference is, that ilinks can be stated inside of paragraphs, like links on websites. The statement has the following parameters:
-
targetStationId
: the id of the station node to be linked to. -
flagName
(optional): if this is set, the flag namedflagName
is set to true when the player touches the button (in addition to switching to the new station node). -
isEnabled
(optional): this can be "true", "false", a flag name or an expression (see above). If the value is "true" or a given flag name is evaluated to "true" (either the flag being set or a value with the same name is set to something not empty), the link is active. Otherwise the link is visible but disabled. If not given, the link is active. -
choiceText
: the text displayed as the choice text.
In some cases, authors might create a possible decision for the player that does not have a necessary direct response in form of text. For example, if a player has the option to pick up an item, it might not be needed to create a new station just for storing the fact that he chose to pick up the item. In this case, it might be enough to just set a flag hasPickedUpItemX
and remain on that station. The button
statement enables autors to create switch buttons like this:
{button(flagName, isEnabled):buttonText}
with the following parameters:
-
flagName
: the flag namedflagName
is set to true when the player touches the button. -
isEnabled
(optional): this can be "true", "false", a flag name or an expression (see above). If the value is "true" or a given flag name is evaluated to "true" (either the flag being set or a value with the same name is set to something not empty), the button is active. Otherwise the button is visible but disabled. If not given, the button is active. -
buttonText
: the text displayed on the button.
Images can be added to the text with an attached styling parameter. This parameter will become the CSS class when the text is rendered inside the StoryQuest app:
{image(cssStylingClass):imageName}
with the following parameters:
-
cssStylingClass
: the CSS class attached to the image for rendering the text display. -
imageName
: the filename of the image.
The default styling includes some simple image style options, more can be added by editing the CSS document:
-
left
: left aligned image with text flowing around. -
center
: centered and scaled to full width. -
right
: right aligned image with text flowing around.
{image(left):someimage.jpg}
Displays an image someimage.jpg
(uploaded using the "Media" tab in the editor) left aligned, with text flowing around.
Boxes contain small excerpts of texts that are detached from the main text. Examples are description of terms or "intermissions". Boxes have an attached styling parameter. This parameter will become the CSS class when the box is rendered inside the StoryQuest app:
{box(cssStylingClass):TextContent}
Box contents can span multiple lines and can also contain Markdown markup. The default styling includes some simple box style options, more can be added by editing the CSS document:
-
left
: left aligned box with text flowing around. -
center
: centered and scaled to full width. -
right
: right aligned box with text flowing around.
{box(left):
### This is a headline
This is an example text. It can span multiple lines.
}
As mentioned above, it is possible to set flags and values inside of QuestML statements. To set a flag, the following statement can be used:
{set:flagName}
This sets the flag named flagName
to "set" or "true". Flag values can be used for the isVisible
or isEnabled
conditions on links or buttons or in scripting blocks (see below). To set a value for a variable, the following statement can be used:
{set(value):variableName}
This statement sets the value value
for the variable variableName
. To read the value, the following statement can be used:
{variableName}
The value of variableName
will be directly inserted where the statement is located, even in headlines or flowing text paragraphs.
{set(Zaphod Beeblebrox):playerName}
This sets the variable playerName
to the value Zaphod Beeblebrox
.
{playerName}
Inserts the content of the variable playerName
at the location of the statement.
{set:hasVisitedTemple}
Sets the flag hasVisitedTemple
to true or "set". Note that flags can be used in conditions like isVisible
or isEnabled
.
Besides setting variables to values, it is possible to increase or decrease a numeric value:
{increase(value):variableName}
for increasing, and for decreasing:
{decrease(value):variableName}
Make sure the variable exists and is initialized befor executing this statement.
List are a flexible way of introducing dynamic into stories without cluttering the text. Lists can be used to insert a changing value into a paragraph. To create a list, the following statement is used:
{listType|element1|element2|element3|...|}
Any number of elements can be used and elements can span multiple lines. Elements can not contain the |
or {}
characters. At the position of the statement, exactly one element of the list is inserted when the station text is rendered. The listType
determines the behaviour of the list and the element to be displayed:
- None (just starting the list with the first
|
): sequence, starting with the first element, each element will be displayed once every time the player visits that station. When the last element is reached, it is always displayed in subsequent visits. - The exclamation mark (
!
): exclusive sequence, same as sequence, but when the final element is reached, nothing is displayed in subsequent visits. - Ampersand (
&
): cycle, when the final element is reached, the sequence starts again with the first element. - Tilde (
~
): random, a random element is chosen every time the player visits the station .
Note: there is a limitation in tracking the current state of a list. If a list with equal entries is used multiple times, the current index is tracked for all incarnations of that list equally. So if a list is used more than one time in different stations, the counter of the list cycle will be the same for each of the equal valued lists.
The dwarf shows you a {|red|blue|green|} gem.
Renders The dwarf shows you a red gem.
on the first visit, The dwarf shows you a blue gem.
on the second, The dwarf shows you a green gem.
on the third visit and all subsequent visits.
The dwarf shows you a {~|red|blue|green|} gem.
The dwarf will show a random colored gem on each visit.
Conditions can be used to show a certain text if a flag or variable is set (or set to a specific value). To use a condition, add the following statement:
{when(flagOrExpression):text}
The text
can span multiple lines. flagOrExpression
can be a flag or an expression. When the flag or expression evaluates to true
(in case of the expression: has a non zero, positive value or a non empty string of characters), the condition is met and the text is displayed.
For expressions, all JavaScript operators can be used. CAUTION: the expression language differs from JavaScript: parameters are enclosed by [
and ]
instead of (
and )
.
Note: the expression of a when condition can be used to distinguish between platforms, especially the app and e-book platform. As the e-book platform does not support scripting features and variables, it is usually needed to create seperate content for these platforms. To enable authors to create both apps (with scripting) and e-books (without scripting) from one single project, the when
statement can be used. The expression can take the keywords isApp
or isEbook
that are evaluated at build time. Thus, an author can create two seperate content blocks, one for each target platform.
{when(hasVisitedTemple):You have already visited the temple and remember it's massive interior.}
When the flag hasVisitedTemple
is true, the text is displayed.
{when(!model.getFlag["hasVisitedTemple"]):You have already visited the temple and remember it's massive interior.}
When the flag hasVisitedTemple
is false ("not hasVisitedTemple
" by the !
), the text is displayed. NOTE: as this is an expression rather than a simple variable name, the JavaScript syntax has to be used!
{when(model.getValue["playerAge"]>70):"You are an old man, like me", says the elder dwarf.}
When a variable playerAge
exists and it contains a number higher than 70, the text is displayed.
script
blocks are a powerful method of introducing advanced scripting into books. Inside of script
blocks, any JavaScript code can be used (but no code blocks enclosed in {}
). Before you use script
statements, consider adding complex code to the onEnter
and onExit
or the globals
section of your station or book. Keep the code in script
blocks simple and clean:
{script:JavaScript code}
Inserts the result of the code evaluation at the position of the statement.
"In ten years, you will be {script:playerAge+10} years old!", the dwarf elder states.
Displays the current age of the player (if stored in the variable playerAge
) plus 10 inside the text.
Dropins are complex components that can be added to a station. Examples include a dice rolling widget or a character data display widget with advanced styling and formatting that can not be achieved using QuestML or pure scripting inside script statements. At this time, dropins must be added to the core StoryQuest system to be usable, so no user-generated dropins are possible yet.
To add a dropin to the station, use the following statement:
{dropin(dropinName, param1, param2, ...):dropInBody}
The dropinName
must match a known dropin. The parameters can be any number of string or number parameters and dropInBody
can span multiple lines.
A list of available predefined dropins and a developer guide for dropins is available here.
{dropin(diceroller, 6):Please touch button to
roll a dice for defending the princess...}
This example could add a dice roller rolling a 6-sided die and the message in body.
The wishlist describes topics that are not covered yet, but will be likely implemented in the future.
- Choices only usable once.
- "Silent choices", content will be "included" seamlessly, possibly with a condition.
- Lists with empty elements.
- Nested lists.
Copyright © 2016 Questor GmbH. Licensed under the MIT License.