Room File Syntax

igium / Liber Ludens / Room File Syntax

Room text files consist of XHTML processed by a template engine and augmented with some syntactic sugar for added functionality, plus a single JSON block, aka room JSON.

Text

Free text

The room text allows for free XHTML use. XHTML is processed by a template engine. Furthermore, all new lines are converted to paragraphs, and functional elements can be present (see below).

New lines and whitespace

A new line is defined by a '\r' character, '\n' character, or a combination of both. Single lines of text are embedded into HTML paragraphs: <p>...line text...</p>. Empty lines are ignored. Other whitespace is not modified and is a subject to the default XHTML treatment.

Reserved characters

The following table lists all characters reserved for defining markup or functional elements and must be substituted with XHTML entities if present in the free text:

character usage XHTML entity
@ starts a non-JSON functional element &commat;
{ starts a JSON functional element &lcub;
} ends a JSON functional element &rcub;
< starts an XHTML tag &lt;

Functional elements

Syntax

All non-JSON functional elements start and end by the '@' character. The JSON functional element starts with the '{'- and end with the '}'- character. Functional elements are:

  • instruction [0..*] (parsed by the book document) - @@INSTRUCTION_NAME arg1,arg2,arg3...@ (arguments are optional)
  • slot [0..*] (parsed by the game view) - @slotName@
  • command [0..*] (parsed by the game view) - @!commandName:argument?text@
  • room JSON [0..1] - (parsed by the book document) - { ... } see the doc section below

Instructions

Dext definitions

@@DEF textId@Single line text.@@ENDDEF@

or

@@DEF textId@
Multiline text.
Multiline text.
Multiline text.
@@ENDDEF@
  • @@DEF textId@ - starts a block of reusable text;
  • @@ENDDEF@ - ends a block of reusable text.

Text definitions' scope is global to the whole game. Trying to redefine a text def with the same textId causes an error. Text definitions from one room file can be used in any room file, including the defining room file.

The contents of a text definition block is not displayed in its place of definition, but can be inserted in a slot by adding the insertText action in a onRoomEnter, onRoomExit, onRoomFinish or a custom on * event in an index-file room definition:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
    "rooms":
    [
        ...
        {
            "key": "_sample2",
            "file": "_room-sample2.txt",
 
            "on insertHere":
            {
                "insertText":
                [
                    { "condition": { "hasSkill": "assassin" }, "textId": "textId1" },
                    { "condition": { "isBefore": "1119.03.27;08:00:00" }, "textId": "textId2" },
                    { "textId": "textId3" },
                ]
            }
        },
        ...
    ]
}

For details on the "insertText" index-file instruction see Index File Syntax.

Text defs cannot contain other functional elements or room JSON:

  • using curly brackets inside a text def raises an error
  • single '@' characters in text defs are copied to the output text, ergo text defs cannot define slots (i.e. no recursive text def insertions)
  • a double '@' character (@@) is interpreted as an instruction start

Nested text defs are not supported, but trying to define a nested text def will not raise an arror. The behavior of the engine in this case is unpredictable.

Slots

Solts define locations in the text where other text is dynamically inserted during the game. The following types of text can appear in slots:

  • Text defined by text defs - inserted with the default formatting of the room text thus becoming a part of the room text output
  • Action text - uses an alternative text formatting, distinguishing action messages from the rest of the text

Slots can be inserted everywhere in the text, but must be located outside text defs and the JSON block.

A slot on a new line:

@slotName1@

Inline slot: @slotName2@

Commands

Commands are functional parts of the room text that are parsed by the view and processed by the player document.

Sample:

@!enterRoomOnce:palace.judgementChambers.judgePerwigAndHammer?to stroke the periwig with your fingers@

New lines in the text part of the command are ignored. The text of the command is copied to the output. It must not contain the '@'-, '{'- and '}'- characters. The text part, including the question mark '?' can be ommitted.

The following commands are currently implemented by the player document:

command argument trigger function
enterRoomOnce roomKey room text onlick navigates to roomKey; triggered only once per game

Template engine

Before being displayed to the user, the XHTML from the text part of the room is processed by an XHTML template engine. Every XHTML tag is tested upon the list of loaded XHTML template files; if there is a template with a name matching that of the XHTML tag, the XHTML tag is replaced with the template's body. In the process all template merge-fields are replaced with a value (see below) or an empty string.

Template files

Templates can be defined in one of the following ways:

  • as a file link in the XHTML page head element (only in markup):
1
2
3
4
5
<head>
...
    <link rel="template" id="templateId" href="template.xhtml" />
...
</head>
  • loaded directly from a file (only programmatically)
1
XhtmlTemplate.loadFromFile(templateId, filePath, callback);
  • loaded directly from an XHTML element (only programmatically) - gets the XHTML element by the specified templateId and loads all of it's innerHTML as template's body
1
XhtmlTemplate.loadFromElement(templateId);
  • using include (only programmatically)
1
include(templateId, filePath);

Merge fields

Merge-fields have the following syntax: {fieldName}, {:processingInstruction} or {fieldName:processingInstruction}.

Merge-fields may appear in free text or in attributes: <p id="{:id}">123<b attr1="aa{field2}">zzq{field1:html}zz</b>aa{field2:html}a<i id="{field3id:id}">{field3:html}</i>{field1}</p>

By default all merge-field values are escaped for XHTML in order to prevent XSS hacks. Processing instructions can be used to change this behavior.

Merge field processing instructions

Merge-fields can define one optional processing instruction. If present, the processing instruction modifies the way the value of the merge-field is selected:

  • no processing instruction - the merge-field is filled with the values provided by the game engine; in all cases except for the built-in templates listed below, the value is selected by matching by name a corresponding tag attribute, or with an empty string if there is no matching attribute;
    • actionListRow - { text: string }
    • navigationRow - { title: string, target: string, ignoreActions: boolean, exitIndex: integer }
    • rootLayout - {}
  • :html - turns off the automatic escaping of XHTML special characters; allows XHTML to be directly inserted into the output
  • :id - inserts an auto-incremented integer that is unique for the current web page; if the merge-field has a name, the id is registered by the game engine, and the name is used internally as an identifier of an XHTML document element; for this to work the {name:id} must be used as the sole value of the element's id attribute: <p id="{myP:id}"></p>

JSON

Every room file can contain zero or one JSON blocks. Trying to add more JSON blocks causes an error. The position of the JSON block is not significant. The JSON block is parsed separately and does not appear in the final text output of the room.

The room JSON has the following schema:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
{
    "title" : "Room title",
    "coverImage": "pathToCoverImage/relatoveTo(appConfig.bookResourcesRoot)from_index.html.jpg",
    "coverImageSize": "cssClassNameOfImageSizeAsDefinedInAppCssFrom(appConfig.bookResourcesRoot)(see[sizeXX_YY]ClassNames)",
    "exists":
    {
        "roomKey1": "Text for the button to navigate to room with key roomKey1",
        "roomKey2": "Text for the button to navigate to room with key roomKey2",
    },
    "knowledge":
    {
        "knowledgeKey1": "Knowledge text 1",
        "knowledgeKey2": "Knowledge text 2",
    },
    "skills":
    {
        "skillKey1": "Skill text 1",
        "skillKey2": "Skill text 2",
    },
    "inventory":
    {
        "inventoryKey1": "Inventory text 1",
        "inventoryKey2": "Inventory text 2",
    },
    "stances":
    {
        "stanceKey1": "Stance text 1",
        "stanceKey2": "Stance text 2",
    }
}

The keys (identifiers) of the following fields from the room JSON are defined in rooms files but have a scope global to the entire game and cannot be repeated:

  • knowledge
  • skills
  • inventory
  • stances

Text texts defined in there JSON blocks are defined in one room file and can be used in any room file, including the defining room file.