Introduction

Rez is a language, compiler, and runtime for writing choice-based interactive fiction/RPG/simulation games based on HTML, Javascript, CSS, and — optionally — graphics, audio, and movie files.

Rez started as a quick alternative to Twine for an author who had become frustrated with Twine.

Twine describes itself as an "open source tool for telling interactive, nonlinear stories." It makes it relatively easy for those with almost no development experience to get started and create a choice-based game.

Rez, by contrast, is designed for making games whose complexity is not well suited to Twine and where a move to a parser based alternative such as Inform or TADS is not desirable. Obvious alternatives to Rez are probably Ink or ChoiceScript.

Rez’s complexity sits somewhere between advanced Twine and Inform/TADS. It has a relatively simple, declarative, syntax but requires the author to be comfortable writing small amounts of Javascript to implement more complex behaviours.

Rez has a standard library including support for creating NPCs, item & inventory management, scenes, and maps and a behaviour tree system to introduce AI behaivour. Rez also features a simple, yet powerful, layout & templating system.

Rez is designed to be flexible enough to create a really complex and ambitious game while offering a simple & usable framework for those just getting started.

Outline of a Rez Game

A Rez game is written in the form of one or more .rez source files that get compiled into an HTML index page & a Javascript application, plus any associated assets like images, movies, and sounds.

A Rez source file contains elements and directives that describe the various components of a game and how it connects together.

At the top level is the @game element that contains the game metadata and all the other elements that make up the game.

Creating a new game

The command:

rez new <game name> --author-name="Name" --author-email="email" --game-title="Title" --game-homepage="URL"

For example specifying <game name> as "mygame" will create a mygame folder containing a number of subfolders. In the src subfolder will be mygame.rez that will contain some example source. In the lib folder will be the stdlib.rez containing Rez standard library. It’s good to familiarize yourself with the contents of stdlib.rez but do not modify it as the game will overwrite it when you compile.

Compiling your game

The command:

rez compile src/<game_name>.rez

Compiles the sources into a game in the dist sub-folder. It creates an index.html as well as copying all of the Javascript & other asset files that constitute the game.

Distributing your game

To distribute your game you distribute the contents of the dist folder. For example by compressing it into a .zip file or wrapping it in an Electron app.

Frameworks

Rez includes two default frameworks:

The files for these will automatically be copied into your dist folder when you compile the game.

Source code format

Rez games are written in plain UTF-8 files with a .rez extension.

The % character is special in Rez and indicates a macro of which the most common is %% for comments.

Comment macro

Rez comments begin with %% and continue to the end of the line.

%% this line will be ignored

Include macro

Once source file may include another by using the include macro %(…), for example:

%(act_one.rez)

An included file may include other files but beware of creating a cyclic dependency. For example this code will hang the compiler:

file1.rez
---------
%(file2.rez)

file2.rez
---------
%(file1.rez)

The Rez Language

Rez is a declarative language for writing a game in terms of a set of elements representating the game contents.

In Rez elements are things like items, actors, scenes, locations, assets and so forth. During compilation Rez draws these elements together and converts them into Javascript objects that represent the game when running in the browser.

Elements are generally described using a set of named attributes. For example an item might have a description attribute that can be displayed to the player when they examine the item.

Rez uses Javascript functions to supply dynamic behaviour. For the most part you can ignore this but, as your game becomes more complex and you want to incorporate dynamic behaviours, you may need to familiarize yourself with writing small Javascript functions.

Here is an example of a Rez element that includes a dynamic attribute:

@item magic_ring {
  is_a: :ring
  magic: true
  material: gold
  owners: 5
  belongs_to: #sauron
  inscription: "Please return to Mordor",
  on_wear: (actor) => {
    if(actor.id == "sauron") {
      actor.game.sauron_victory = true;
    } else {
      actor.makeInvisible();
      actor.corruption += 10;
    }
   }
}

There’s a lot going on here but we’ll unpack it piece by piece.

Introduction to Elements

In the first place is the element itself:

@item magic_ring {
  ....attributes....
}

There is a common pattern for writing elements:

  • element specifier with @ prefix, e.g. @item

  • a unique ID of the element, e.g. magic_ring

  • open brace {

  • attributes

  • close brace }

Directives, by contrast, may look a little bit different, e.g. they don’t have a unique id.

Elements are used to describe in-game concepts. The Element Catalog describes each element in detail.

The id of an element must be unique and follow the rules for Javascript identifiers. In JavaScript, identifiers are case-sensitive and can contain Unicode letters, $, _, and digits (0-9), but may not start with a digit.

In some situations you may want to use similar ids for different kinds of elements, in this case a helpful protocol is to prefix the id with the type, e.g. instead of #emergency_exit you might use #s_emergency_exit for a scene or #c_emergency_exit for a card.

Introduction to Attributes

The most important thing when you are writing an element is its attributes. These describe the element and how it behaves in the game. In our @item example there are 7 attributes that demonstrate many of the built-in types:

is_a: :ring
magic: true
material: gold
owners: 5
belongs_to: #sauron
inscription: "Please return to Mordor",
wear: (actor) => {
  if(actor.id == "sauron") {
    actor.game.sauron_victory = true;
  } else {
    actor.makeInvisible();
  }
}

There are seven attributes defined here:

is_a

a keyword, a symbol often used when there are a few legal values

magic

a boolean

material

another keyword

owners

a number

belongs_to

a reference to the ID of an element

inscription

a string

wear

an event script in Javascript arrow function format

The pattern for any attribute is <name>: <value>. The space after the colon is required and note that there is no , or ; at the end as you may be familiar with from other programming languages.

Legal

title: "The Maltese Parrot"

Not-legal

title : "The Maltese Parrot"
title :"The Maltese Parrot"
title:"The Maltese Parrot"

Attribute names follow the rule for Javascript identifiers:

  • cannot contain spaces

  • are case sensitive

  • must begin with a letter, underscore _, or dollar $

  • can only contain letters, numbers, underscores, or dollar signs

Note that attribute names with a leading underscore (_) are considered to be 'internal' to the Rez compiler. These attributes are not converted into runtime attributes and are, therefore, not available.

Attribute names with a leading dollar ($) are considered to be 'special' and it is not advised to use them yourself unless you know what you are doing. Rez itself makes use of attributes with the $ prefix for housekeeping and you could, inadvertently, trample these.

Rez defines many attribute types, some simple and some more complicated. The more complicated types are generally related to creating dynamic behaviour and may require additional Javascript knowledge:

Boolean

a truth value that you can test to create conditional behaviour

true|false (not that yes and no can also be used interchangably)

Number

a numeric value that can be positive, negative, integer, or decimal. Rez doesn’t have separate types for these.

5|-1|0.5

String

a text value suitable for shorter strings. For longer passages a Heredoc may be easier

"it’s a plaster bust of a parrot"

Keyword

a symbol, usually used for constant values. Note that keywords can be turned into hierarchies by the @derive directive

:single|:multiple, :red|:green|:blue

Element Reference

an identifier referring to the unique id of an element

#sauron, #gandalf, #the_ring

Attribute Alias

References an attribute in another element

&sauron.location

Heredoc String

a text value that can span across multiple lines

"""it’s a plaster bust of a parrot"""

File

a string value that is imported from a file

<<<FILE_NAME>>>

Template

a text value that can span multiple lines and content template expressions that are dynamically interpolated at runtime

```The ${animal.adjective} ${animal.color} ${animal.species} jumped over the ${other_animal.adjective} ${other_animal.species}```

List

a sequence of other values, that can be of any Rez attribute type, inside []. Note that Rez lists do not use a , to separate values.

[1 2 3], ["red", "green", "blue"]

Set

an unordered collection of unique values of any Rez attribute type. Note that rez Sets do not use a , to separate values.

#{:red :green :blue}

Table

a collection of name: value pairs where the values can be of any Rez attribute type. Note that Rez tables do not use a , to separate name: value pairs.

{color: :red size: :large print: "Danger"}

Script (Event)

a Javascript function for handling an event. Expected to be in arrow format and passed the object receiving the event and the event as parameters.

(game, event) ⇒ {…​}

Script (Action)

a Javascript function expected to be called, e.g. in an event handler. Expected to be in traditional function style and where this is the object in question.

function() {…​}

Behaviour Tree

A behaviour tree is an alternative to Javascript for creating dynamic behaviours. See [behaviours] for more information about using behaviour trees.

^[behaviour {options} children]

Dice

a dice roll, in Dice Notation that is re-evaluated each time it is referenced

2d6+1, d4, 3d6-1, 2d10

Probability Table

A list of pairs wrapped in | that becomes a generator property.

|"key_1" freq_1 "key_2" freq_2 "key_3" freq_3|

Tracery Grammar

a text value whose contents should be a Tracery grammar

G``{origin: ...}```

Binding Path

Used within bindings to specify an object via a property path.

`source.exits

Code Block

A Javascript expression. It is converted into an expression function(obj) {return (<block>);}

^{Math.rand_int(1,10)}

Dynamic Initializer

A Javascript expression that is evaluated when the game starts

^i{Math.rand_int(1,10)}

Dynamic Property

A Javascript function expression that is converted into an object property

^p{return this.first_name + " " + this.last_name}

Boolean

A boolean value is either true or false (alternatively we can use yes and no) and is often used for flags.

The underlying data representation is a Javascript boolean.

Number

A number value can represent either integers or floating point values.

The underlying data representation is a Javascript number.

String

A string value is text enclosed with double-quote (") characters used for descriptive properties. Typically single lines, where multiple lines need to be used the suggestion is to use the Heredoc string instead.

The underlying data representation is a Javascript string.

Keyword

A keyword value is a special kind of string primarily used for identifier values. It is prefixed with a colon (:) and must obey Javascript identifier rules.

The underlying data representation is a Javascript string.

Element Reference

An element reference is used to refer to the id of a game element. It is prefixed with a hash (#) and must obey Javascript identifier rules. Although it acts like a string part of the value of element references is that the compiler will attempt to verify that they refer to an existing object.

The underlying data representation is a Javascript string.

Attribute Alias (Deprecated)

An attribute alias is used to refer to an attribute of a specific element. It is prefixed with an ampersand (&) and consists of elem_id.attr_name where elem_id is an element id and attr_name is the name of an attribute of that element.

The underlying representation is a Javascript object {elem_id: <elem_id> attr_name: <attr_name>}.

Heredoc String

A heredoc string is a multi-line capable string that is whitespace aware.

File String

A file string is a string value whose content is stored and read in from an external file.

Template

A template is a kind of string value that supports dynamic content that is interpolated at run-time. This is controlled by the use of expressions such as ${…​}, $if() {% …​ %}, and $foreach(x: xs) {% %}. See template expressions for more.

List

A list of whitespace separated values that can include any of the other attribute types. It is separate from a @list element.

Set

A set of whitespace separated values that can include any of the other attribute types.

Table

A series of key: value pairs where the key should be a Javascript id and the value can be any of the other attribute types including another table.

However it is worth noting that using deeply nested tables is not advised. It does work, but the entire set of tables is the attribute making working with nested values more complicated.

Event Script

An event script is written as a Javascript arrow function (args) ⇒ {…​} and therefore this will be null when it runs. Typically the object the event has been triggered for will be the first argument.

Action Script

An action script is written as a regular Javascript function function (args) {…​} and this will refer to the object the script has been defined on.

Dice

Probability Table

Esp. useful for procedural generation a probability table is a list of pairs where the first element is the key and the second is the frequency. Let’s take eye color for example, we want characters we generate to have different coloured eyes. In reality brown eyes are most common at about 48% of the population, then blue at 29, green at 14%, and grey at about 9%. How could we generate a realistic distribution of eye colour (very important in games):

eye_color: |:brown 48 :blue 29 :green 14 :grey 9|

A different example might be a loot table, how could we generate one of those:

loot_quality: |:poor 20 :okay 10 :great 5 :amazing 1|

Our frequencies don’t have to % based and add up to 100, in this example we’ve given relative frequencies.

We can also use `#id’s as the key:

meet_on_the_road: |#ranger 15 #wizard 10 #traveller 45 #evil 30|

At the moment, due to a lack of JSON support, it is not possible to use attribute refs or functions as entries. A work around looks like this:

@card card1 {
    content: ```
    ${f}
    ```

    func_table: |#o1 50 #o2 25 #o3 25|

    choose_f: function() {
      return $(this.func_table).f();
    }
  }

  @object o1 {
    f: function() {
      return 1;
    }
  }

  @object o2 {
    f: function() {
      return 2;
    }
  }

  @object o3 {
    f: function() {
      return 3;
    }
  }

It’s not elegant but it’s feasible. This will likely get cleaned up in a future version.

Tracery Grammar

  • TODO

Code Block

A code block uses the form ^{…​} and can contain a legal Javascript expression.

The code block is implicitly transformed into a function of 1 argument obj and that returns the value of the expression.

Dynamic Initializer

A dynamic initializer uses the form ^i{…​} to run an expression once at the time the object is created.

This is useful for setting a generated value (e.g. a random value) after which the attribute behaves normally using getters/setters.

Note that this is not a function, the initializer uses the last expression as the value. In the following example we name an actor using a randomly generated given & family name.

@actor random_npc {
  name: ^i{
    const given_name = $("given_names").randomElement();
    const family_name = $("family_names").randomElement();
    `${given_name} ${family_name}`;
  }
}

Dynamic Property

A dynamic property is a property generated from an expression in the form ^p{} for example:

@actor random_npc {
  class_name: ^p{
    return this.class === "g" ? "Gunslinger" : class === "s" ? "Sleuth" : "Crook";
  }
}

What’s in a Game?

The simplest possible Rez game would look something like this:

@game {
  name: "Test Game"
  IFID: "D3C31250-53B4-11ED-9A26-3AF9D3B0DD88"
  archive_format: 1
  initial_scene_id: #play_game
  layout_mode: :single
  layout: ```
  ${content}
  ```

  %(stdlib.rez)

  @scene play_game {
    initial_card: #did_you_win
    layout_mode: :single
    played: 0
    won: 0
    win_p: 0
    layout: ```
    <div class="container">
      $if{scene.played > 0} {%
        <section class="hero is-primary">
          <div class="hero-body">
            <p class="title">Winning Percentage: ${scene.win_p | round: 0}%</p>
            <p class="subtitle">
              $if{scene.win_p >= 50.0} {%
                You are a winner!
              %}, {%
                You are a loser!
              %}
            </p>
          </div>
        </section>
      %}

      <p>Played: ${scene.played}</p>
      <p>Won ${scene.won}</p>

      ${content}
    </div>
    ```
    win: function() {
      this.played += 1;
      this.won += 1;
      this.win_p = this.won * 100 / this.played;
    }
    lose: function() {
      this.played += 1;
      this.win_p = this.won * 100 / this.played;
    }
  }

  @card did_you_win {
    content: ```
    Did you win? [[yes|yes_i_won]] | [[no|no_i_lost]]
    ```
  }

  @card yes_i_won {
    content: ```
    Congratulations!

    [[Play again|did_you_win]]
    ```
    on_start: (card) => {
      card.scene.win();
    }
  }

  @card no_i_lost {
    content: ```
    Better luck next time!

    [[Play again|did_you_win]]
    ```
    on_start: (card) => {
      card.scene.lose();
    }
  }
}
test game 1
test game 2

This is a terrible game but it illuminates some of the basic principles of how you create a game using Rez.

It uses 3 types of element: @game, @scene, and @card. The scene has some attributes to keep track of the game state and two actions, the cards use an event handler and some template links.

The @game is a required top-level element that contains the definintion of the game and holds the master layout into which scene content is inserted, and the reference to the scene that starts the game.

A game must have at least one @scene. A scene represents a context where specific events or interactions take place. It must also have an initial_card attribute that defines which card is played into the scene when it starts. You can run your game from a single scene or use multiple scenes where it makes sense to do so.

Lastly the cards, which are "played" into the scene, and which provide the bulk of the content presented to the player.

So we have a structure:

@game/layout
  @scene/layout
    @card/content

The card content is rendered into the scene layout, and the scene layout is rendered into the game layout. You might notice the scene has a layout_mode attribute. In this case we are using the single layout mode that presents only the current card. There is also a stack layout mode that presents all of the cards played into the scene.

The scene in this case defines two script attributes win and lose that update the score and winning percentage. These are called from the on_start event handler of the cards yes_i_won and no_i_lost. The event handlers are Javascript arrow functions that take their source object (and, optionally, an event object) as a parameter. The scene scripts are regular functions where this is the object in question (in this case the scene play_game).

You can use Markup for simple formatting although here we are showing off some of the Bulma CSS classes. We also using template expressions to display variables ${} and conditionally present content $if{} {% …​ %}.

You can go quite a long way using only this subset of Rez’s features.

What’s Going on in the Browser

We should distinguish between two environments: The author_time environment where we’re dealing with .rez source files containg elements & attributes, and the runtime environment where these have been compiled into JavaScript code that runs in the browser.

runtime.js

All of the functionality of the game is converted into Javascript objects and functions which end up in a file called runtime.js. You can see this in the dist/assets folder of your game. It’s worth looking through runtime.js because you can see all of the library classes and functionality. Note that you should never modify runtime.js as it will be overwritten the next time you compile your game. However, in practice, there should be no reason to modify this file as its contents are produced from your game.

In the runtime environment, your @game element is translated into a JS object with RezGame as its prototype, the scenes into JS objects with RezScene as its prototype, and cards into JS objects having RezCard as their prototype. For most elements there is a 1:1 correspondence between it and an equivalent JS object defined in runtime.js.

[Advanced Note]: If you want to use different objects you can use the $js_ctor attribute to define which constructor function gets called. When replacing built in objects its advisable to have the built-in object as a prototype of your custom object.

The Game starts with a called to the game object start method which handles initialization and presenting the first scene & card.

The Rendering Process

The HTML that is presented in the browser is generated as follows:

At the top level the @game element requires a layout: template attribute. It further requires that this template contains a ${content} template expression. Internally the game uses a RezSingleLayout object to render the current scene, which it adds to the layout bindings as content. So the scene content is inserted into the game layout as ${content}.

At the next level down the @scene also requires a layout: template attribute and, it too, requires a ${content} template expression to be present. The scene either uses a RezSingleLayout (layout_mode: :single) or a RezStackLayout (layout_mode: :stack) depending on whether the scene is based on one @card or many @cards. The layout renders the card content and places it in the layout binding content. So the card content is inserted into the scene layout as ${content}.

At the next level down the @card provides a content_template: and, optionally, flipped_template: attribute. The flipped template is used in the stack layout which we’ll discuss shortly.

So in the simplest case the structure is:

Game Layout
  Scene Layout
    Card Template

The actual picture can be a little more complicated because the scene layout and card can also include content from other cards by specifying the id of the cards in their blocks: attribute. But what is a block?

What is a Block?

Using the blocks: attribute we can specify the attribute of cards that we want to include beyond the main content card. For example, to include a sidebar that is common across cards in a scene:

@card sidebar {
  content: ```
    sidebar content goes here
  ```
}

@scene explore {
  blocks: [#sidebar]

  layout: ```
  <div class="sidebar">${sidebar}</div>
  <div class="main">${content}</div>
  ```
}

When the explore scene gets rendered it will render its current card and bind the rendered content to content and also render the card #sidebar and bind that content to sidebar. So using the ${sidebar} template expression from the layout includes the sidebar content.

Note that when a card is used from a block: attribute it is automatically given a $parent_block binding (that points to the card using it as a block) so that it can refer to the attributes of its parent card.

This is useful when you want to create a "parameterized" block. For example, we could dynamically render a list of available exits in a card representing a location, this way:

@card list_exits {
  bindings: [
    location: $block.$parent_block.source
  ]
  content: ```
    $if(location.exits) {%
      $foreach(exit: location.exits) {%
        %% render an exit here
      %}
    %}
  ```
}

@card room_with_exits {
  exits: [#exit_1 #exit_2 #exit_3]
  blocks: [#list_exits]
  content: ```
    Room
    ${exits}
  ````
}

@card another_room_with_exits {
  exits: [#exit_4 #exit_5 #exit_6]
  blocks: [#list_exits]
  content: ```
    Another Room
    ${exits}
  ```
}

In this example #room_with_exits and #another_room_with_exits both define an exits: attribute and render the card #list_exits as a block.

However, #list_exits doesn’t have to know which card is rendering it, only that it defines an exits: attribute.

We use a code-block binding location: from the #list_exits card to reach up to its 'parent' card (the one that included it as a block) to find its exits: attribute and use that for rendering the list of exits.

This means we can use #list_exits from any card that defines an exits: attribute.

What are Bindings?

Bindings are how we make data from our game elements available to the code and templates that are rendering our view. You’ve already seen an example of a binding in the section above:

bindings: [
  location: $block.$parent_block.source
]

But what does this mean? And why do we need it?

Let’s go back to basics with a very simple content template, e.g.:

@card c_test_1 {
  content: ```
    Hello from the first chapter
  ```
}

This will present the text "Hello from the first chapter" in the browser.

But how does this happen?

The next section is quite technical and will likely require a good understanding of Javascript, far more than is required to use bindings & expressions. Feel free to skip ahead if you don’t feel comfortable digging this deep.

Rez converts this simple markup into a Javascript function that renders it. For the content above you’d end up with something like

function(bindings) {
  return [
    function(bindings) {
      return `<div id="card_c_test_1" data-card="c_test_1" class="card">Hello from the first chapter.</div>`;
    }
  ].reduce(
    function(text, f) {
      return text + f(bindings)
    },
    ""
  );
}

Now you might be thinking "OMG! Why do we need such a complex function to render a simple line of text?"

If every template was as simple as this, we wouldn’t. But a simpler approach wouldn’t allow us to build more complex, dynamic, templates. Before we get to that, let’s break down this function.

The outer level is a function that accepts an argument bindings and then returns the result of a reduce() call on an array. In this case an array containing a single function also taking bindings as its argument.

This 'inner' function doesn’t use its argument, it just returns the static string that is our output.

The reducer function takes some text (initially an empty string) and a function (taking a bindings argument), and returns of appending the function result to the text.

The result is that we "thread" the outer bindings variable through the inner function and concatenate the results.

So essentially this boils down to:

"" + `<div id="card_c_test_1" data-card="c_test_1" class="card">Hello from the first chapter.</div>`

And, hence, to our output.

To see why it works this way, let’s look at a dynamic template using a template expression:

@card c_test_2 {
  chapter: "second"
  content: ```
    Hello from the ${card.chapter} chapter.
  ```
}

This template breaks down into three chunks:

  • "Hello from the "

  • ${card.chapter}

  • " chapter."

The first and last chunk are simple strings, like our previous example. But the middle chunk is a template expression that must be generated, using some Javascript, when the card is being rendered. At that time the value of card.chapter is "second" so the template is equivalent to an expression like:

"Hello from the " + "second" + " chapter."

Let’s look at the rendering function generated for this template:

function(bindings) {
  return [
    function(bindings) {
      return `<div id="card_c_test_2" data-card="c_test_2" class="card">Hello from the `;
    },
    function(bindings) {
      return (function(bindings) {
        return bindings.card.chapter;
      })(bindings);
    },
    function(bindings) {
      return ` chapter.</div>`;
    }
  ].reduce(
    function(text, f) {
      return text + f(bindings)
    }, ""
  );
},

It’s more complicated but follows the same exact pattern, running reduce() over an array, that now contains three inner functions. These functions return the following content respectively:

`<div id="card_c_test_2" data-card="c_test_2" class="card">Hello from the `
`second`
` chapter.</div>`

That is concatenated to present the user with "Hello from the second chapter."

But how is "second" getting from the chapter: attribute of the card into the second inner function. It happens through the bindings argument that we are threading through the outer rendering function to those inner functions. Let’s look at the second inner function:

function(bindings) {
  return (function(bindings) {
    return bindings.card.chapter;
  })(bindings);
}

If we strip away the mechanism here the core part is:

return bindings.card.chapter;

This is what the template expression ${card.chapter} boils down to.

This inner function is using it’s bindings argument to look up card.chapter. card is an example of a default binding that Rez makes. Whenever a @card is rendering it binds card to the RezCard object representing that card. In this case the object that defines the chapter property, containing the string "second". Rez also automatically binds scene to the current RezScene and game to the RezGame instance. So you can always use expressions involving card, scene, or game bindings without needing to bind anything yourself.

But, by itself, the rendering system knows nothing about your game world and the elements you have populated it with. For example, you may have an @actor element with id #player that has a name: attribute, but the renderer doesn’t know about that. In order to use an expression like:

${player.name}

We have to teach Rez how to point player at the right object. That’s where bindings and the bindings: attribute come in. They bind a variable name that you can use in a template to the Javascript object containing the values you want to refer to. To make the expression above work we’d use:

@card c_player_name {
  bindings: [player: #player]
  content: ```
  Your name is ${player.name} and a very fine name it is too!
  ```
}

This is an example of an 'element binding'.

Element Bindings

The simplest form of binding is to bind a variable name to the game object representing an element. In the example above we’re looking for the name: attribute of some element with the id #player. We can make this work by binding the player variable as follows:

bindings: [player: #player]

Here we’re teaching Rez to make a binding to the Javascript reference (player) representing the given element id (#player). With this binding in place we can refer to player in our templates.

Function Bindings

Sometimes we want to be able to refer to something that isn’t an element with a a fixed id. Two common reasons are:

  • we want to refer to a dynamic value

  • we want to refer to a dynamically choosen element (i.e. we don’t know the id at authoring time)

  • we want to refer to something that doesn’t have an id, such as a collection of objects

For these situations we have function bindings. Here we bind a variable name to the return value of a function written inline in the bindings. Here are some examples:

bindings: [
  random_number: () => {return Math.rand_int(10)}
  weapon: () => {return game.get_weapon($player.favourite_weapon_id);}
  exits: () => {return $player.location.exits(true);}
]

In each case the variable will be bound to the return value of the function.

Note that these bindings are re-created each time the template is rendered so while weapon and exits might have the same values, random is going to have a different value each time.

Attribute Bindings

Attribute bindings are a convenience when you want to refer to a specific element attribute.

bindings: [
  name: &player.name
]

Path Bindings

A path binding is used to refer an object by a key-path from the $block object. This is mainly useful when implement cards intended to be used as bound blocks, that want to refer to their parent card context.

bindings: [
  exits: `$parent_block.source.exits
]

All path-references implictly begin with the $block variable (that refers to the card currently being rendered). So $parent_block refers to the $parent_block attribute of the current $block.

Using a path binding we can get to the parent card which may be one of many cards (why we can’t use an element reference) and its attributes.

Sharp Edges

Path bindings are often used to get at the internal mechanics (parent blocks, sources and so on) which are already a little complicated.

Note that unlike previous versions it is now possible to make bindings that refer to previous bindings, so:

bindings: [
  player: #player
  name: player.name
]

Is legal, however you can only refer to previous bindings made in the same bindings block.

Stack Layout, Flipped Cards, and Blocks

By default a @scene specifies a layout_mode: of :single which means that the scene renders a single 'main' @card as its content. When a new card is played into the scene it replaces the previous card and the view gets re-rendered.

However, there are times when when you might want to render more than one card into a scene. For example a dialogue scene might represent a number of interactions back and forth between characters with the player able to specify a response. In these, and similar examples, you don’t want the "history" of the scene to disappear.

To achieve this a @scene can specify layout_mode: :stack to use the RezStackLayout. When using the stack layout, playing new cards into the scene do not replace the exist card but are appended or pre-pended to the list of previous cards (based on the layout_reverse: attribute).

When the RezStackLayout renders, it renders the list of cards played into the scene (separated by any content in the layout_separator: attribute).

However, in fact, an author probably doesn’t actually want to re-render previous cards. A card that presented a set of dialogue choices doesn’t make sense when the player has already made their choice. It would make more sense to render a version of the card representing the choice the player has made.

This is why cards support a flipped_content: attribute. When a new card is played into a scene with a stack layout the previous card gets 'flipped' and renders the flipped_content: template rather than the content: template.

But what happens if we play the same card multiple times? How does it know which is flipped and which is 'face up'. What happens if an event wants to store data in the card? To answer these questions we need to go a little deeper.

The rendering process doesn’t directly render @card`s, `@scene`s, or `@game’s. Rendering is done via an object whose prototype is `RezBlock. RezSingleLayout and RezStackLayout both have RezBlock as their prototype. For each @card that is being rendered there is an instance of RezBlock.

A RezBlock handles generating HTML to output to the view by calling executing it’s template with appropriate bindings. Where appropriate a block also has a parent_block reference that allows walking back up the content tree. (See the example above related to bindings).

So when a RezCard is added to a RezStackLayout it’s actually the card wrapped in an instance of RezBlock. The same card can get added to the layout many times, it’s always the same card, but different block instances.

What this means is that when a card is being flipped it’s actually the block that tracks flipped status and decided whether to render its cards content: or flipped_content: template.

Further it means that when an event wants to track how this changes the cards content it can store those changes in the block.

@card next_move {
  content: ```
  <a href="javascript:void(0)" data-event="shoot">Take a shot</a> or <a href="javascript:void(0) data-event="flee">Flee</a>.
  ```

  flipped_content: ```
  $if($block.action == "shoot") {%
    You shoot and ${block.hit | alt: "hit", "miss"}.
  %}, {%
    You run for it.
  %}
  ```

  on_shoot: (card, evt) => {
    card.current_block.action = "shoot";
    card.current_block.hit = $player.hits_with_primary_weapon();
    return {
      card: "next_move"
    };
  }

  on_flee: (card, evt) => {
    card.current_block.action = "flee";
    return {
      card: "run_away"
    };
  }
}

The first time the next_move card is added to a scene it displays the options to shoot or flee. There are two event cards which set the choosen route into the block and in the case of shooting what the result was.

When the card is re-rendered the flipped_content: template is rendered which uses the block properties action and hit to decide what should get rendered.

HTML Structure

When the game layout gets rendered its content is embedded inside a built-in template:

<div class="game">
    ...game layout...
</div>

You can target the whole game content using the game CSS class.

The game layout is a good place to put fixed parts of the interface, for example titles, score, current time or location, and so on. The game layout is expected to contain the template expression ${content} which will include the contents of the current scene.

When the current scene gets rendered its content is embedded into a different template:

<div id="scene_<scene-id>" data-scene="<scene-id>" class="scene">
  ...scene content...
</div>

In the same was as the game, the scene layout is expected to contain the template expression ${content} which will include the contents of the current card or (in stack mode) cards. You can style scenes by targetting the scene CSS class or customise styles for particular scenes by targetting the DOM id. In our example game that would be scene_play_the_game.

When a card gets rendered its content template is embedded within the following template:

<div id="card_<card-id>" data-card="<card-id>" class="card <card-type>">
   ...card content...
</div>

One thing to note is that the scene_id may not be what you expect. If the current scene was set to #explore_office you might expect that the rendered HTML would contain this id. However Rez treats your @scene and @card elements as a template and uses a copy when rendering a scene.

==

Block content comes from cards that are being rendered inside another card. For example you might have a card #sidebar that we want to use to render sidebar content that should always be visible.

In this case we would add it to (for example) the scenes blocks: attribute. To include it within the scene layout you would use the template expression ${sidebar}.

Scene Layout Mode

A @scene has a required attribute layout_mode: which can, as of v0.11, have two values:

  • :single

  • :stack

In :single mode the ${content} substitution embeds the content of the current card in the scene. When the card changes the content will change to match it. The effect is that the scene will jump from card to card.

In :stack mode the ${content} substitution embeds the content of every card that has been played into the scene so far. Rather than jumping from card to card the cards will accumulate.

However, as a new card is played the previous card gets "flipped". What that means is that instead of rendering the content attribute it renders the flipped_content attribute.

For example a card might present the player with two options. If the card didn’t get flipped it would continue to present two options even though an option had been selected. But the flipped version can, instead, display the chosen option.

Linking to cards, scenes & events

Playing a card

When we play a card into the current scene we are either replacing (scene layout_mode: :single) or adding (scene layout_mode: :stack) to the content in the scene.

<a href="javascript:void(0)" data-event="card" data-target="play_game">Play Again</a>

This will create a link titled "Play Again" that plays the card with id #play_game.

Switching to another scene

A scene switch is when we end one scene and begin another, automatically playing its initial card.

<a href="javascript:void(0)" data-event="switch" data-target="fight">Draw your gun</a>

This will create a link titled "Draw your gun" that will end the current scene and begin the scene #fight.

Creating an interlude

An interlude is when we interrupt one scene to play out another, and when that scene ends returning to the original scene.

<a href="javascript:void(0)" data-event="interlude" data-target="store">Shop at the store</a>

This will create a link "Shop at the store" that interrupts the current scene and starts the scene #store. This should be followed by a resume to return to the original scene.

An example of where this kind of link is useful is for presenting a player inventory. Looking at the inventory steps out of normal gameplay. When the player is done with the inventory they expect to be back where they were before they triggered it.

It is possible to have an interlude within an interlude but may get confusing if taken too far.

Resuming the previous scene

From an interlude we can resume the previous scene using a resume link.

<a data-event="resume">Leave the store</a>

This will end the interluded scene and resume the previous scene where it left off.

There may be situations where you only want links to appear under specific circumstances. You could do this a template expression but Rez has a built-in facility for dynamic links. Using the syntax:

Triggering events

A link can trigger a custom event.

<a data-event="reload">Reload gat</a>

This will create a link titled "Reload gat" that when clicked will run an event on_reload on the game, scene, or card (in that order).

Once the event handler has done its work it should return a response object.

Passing data

Any of the previous types of link can be amended to pass arbitrary data values. For example we might have a dialogue scene and want to control which actor the player is going to have a dialog with:

<ul>
  <li><a data-event="switch" data-target="conversation" data-actor_id="gutman">Speak with Gutman</a></li>
  <li><a data-event="switch" data-target="conversation" data-actor_id="wilmer">Speak with Wilmer</a></li>
</ul>

When either link is clicked it will start the new scene #conversation and that scene will have it’s actor_id attribute set to either #gutman or #wilmer based on which of the links is clicked. This offers a great deal of ability to customise the behaviour of cards and scenes.

Event Reponse Objects

Return an object from an event handler to determine what happens next. Some object types can be combined (e.g. the flash message combines with most of the other choices)

{scene: "scene_id"}

To start a new scene.

{card: "card_id"}

To play a new card into the current scene.

{flash: "message"}

To set a flash message.

{render: true}

To have the current view re-rendered.

{error: "message"}

To log an error message to the console.

Buttons

An alternative to using a link is to use a <button> with a data-event attribute. For example a button to play a new card would look like:

<button data-event="card" data-target="new_card_id" class="button">Load Card</button>

By specifying data-event="card" we tell the button it’s loading a new card and the data-target attribute specifies which card to load. We can use a similar approach to load new scene:

<button data-event="switch" data-target="new_scene_id" class="button">Switch Scene</button>

Here data-target specifies the id of the scene to switch to. Use data-event="interlude" for an interluded scene, rather than a scene switch.

Where you want to run a custom event handler, on_something_interesting, use specify the event name directly in the data-event attribute:

<button data-event="something_interesting" data-custom-value="..." class="button">Something Interesting!</button>

You would pair this with an event handler as follows

on_something_interesting: (card, evt) => {
  const custom_data = evt.target.dataset.custom_value;
  // Interesting processing happens here
  // then...
  // what should happen next?
  return {
    render: true
  }
}

In this example the handler is in a card but you can also put in the scene or game as appropriate.

Sometimes you want a link to be disabled based on dynamic criteria (the bar doesn’t open until 8am) or maybe not even to appear at all (the portal entrance isn’t visible if you’re not wearing your x-ray specs).

To make a dynamic link use the dyn_link template expression filter. Here’s an example:

@card {
  content: ```
  ${card | dyn_link: "rest"}
  ```

  on_rest: function(dyn_link) {
    if($player.is_fully_rested) {
      dyn_link.deny("You are already rested");
    } else {
      dyn_link.allow("Rest", "player_rests");
    }
  }
}

In this case, if the player is already rested they are shown a disabled option. In some cases it might be preferable to use dyn_link.hide() so that no choice is offered at all.

The event handler is passed a RezDynamicLink object that it can use to customise link presentation.

Forms

An HTML interface will often use form controls to allow the player to input or interact with data. A simple example would be using an <input> to accept a characters name. Rez offers a number of ways to support using forms.

Binding form elements

For data capture the simplest approach is to bind an HTML form input element to an attribute value using the rez-bind attribute.

textfields and textareas

To bind an input with type='text' or a textarea:

<input type="text" rez-bind="player.name">
<textarea rez-bind="player.description">

This sets up a two-way binding between the content of the <input> and the player.name and player.description attributes respectively. For example, whatever is entered into the name form input will be set directly on the player.name attribute. Equally assigning to the attribute $("player").name = "…​" will update the input field.

checkboxes

You can bind a checkbox input to a boolean attributes.

<input type="checkbox" rez-bind="player.isOver18">

radios

You can bind a set of radio buttons to an attribute.

<input type="radio" name="class" value="detective" rez-bind="player.class">
<input type="radio" name="class" value="hood">
<input type="radio" name="class" value="dame">

Note that radios with the same name attribute will form a group and you only need to bind the first radio in the group.

select drop-downs

You can bind a <select> to an attribute:

<select rez-bind="player.gender">
  <option value="m">Male</option>
  <option value="f">Female</option>
</select>

Adding events to inputs and forms

For more complex interactions use the rez-live attribute to generate events.

<input name="name" rez-live >

When the user changes the value of the field this will generate an on_input event on the corresponding RezCard object, passing the generated event as a parameter.

<form rez-live>...</form>

Will generate an on_submit event to the form. The handlers in either case should return as any other event handler. In the case of submit it is probably to load a new card or scene.

Assets

Assets are files that you want to include in your game for example images, audio files or movies. Rez handles copying these into your game distribution folder and generating appropriate references.

You declare an asset with an @asset element:

@asset pistol_image {
  file: "pistol_image_01.png"
  width: 60
  height: 60
}

Rez handles finding the asset file and making it available in the dist folder. Now if you want to include it you have two options, both using template expressions.

${"pistol_image" | asset_tag}

Because the asset is an image this will generate an <img /> tag that points to the image file relative to the game file.

As of v0.11 only image files are supported but sound & movie support will be included soon.

The second approach is to generate a path and build your own tag:

<img src='${"pistol_image" | asset_path}' />

This will work for audio & movie assets.

Template Expressions

Template expressions are how you include dynamic content in your game user interface. They work in @game & @scene layout attributes and in a @card`s `content and flipped_content template attributes.

Template Expressions are loosely based on the Liquid markup system. But it’s worth noting that they are not actually Liquid and you should always refer to this documentation not the Liquid docs.

There are three kinds of template expression.

Subsitution Expressions

A substitution is where we replace a token like ${player.name} in a template with the value of the expression. For example:

content: ```Your name is ${player.name}. It is a good name.```

If the player objects name attribute is "matt" this will return:

Your name is matt. It is a good name.

Note that the an expression is only a lookup. You cannot use arbitrary JS expressions, so:

content: ```Your name is ${player.name + "!"}```

Will not work. If you want to modify the value you must use a filter expression (see below) to do so. In this case it would be:

content: ```Your name is ${player.name | append: "!"}```

Where does this player reference come from? Good question, this is an example of a binding. You’ve already seen bindings at work with ${content} and ${sidebar}. content is an example of a binding that Rez automatically makes available but you can add your own to refer to any objects you like.

bindings: [
  player: #player
]
content: ```Your name is ${player.name}```

Here we are binding the Javascript variable player to an element with id player (which we might assume is an @actor element defining the player character). For example:

bindings: [
  player: ^{$("player")}
]

is an equivalent way of creating the same binding. If we didn’t know the object we wanted to bind to in advance we can use a dynamic binding with a function.

bindings: [
  actor: ^{$("npc_list").randomElement()}
]

But you don’t have to make bindings only to elements, you can bind to any Javascript value:

bindings: [
  coins: ^{Math.clrand_int(25)}
]
content: ```
You found ${coins} coins on the floor and put them in your pocket.
```

Default Bindings

In the context of a template there are usually default bindings:

  • $block - the current rendering block, the element it represents is usually in its source property

  • card - the RezCard of the card being rendered

  • scene - the RezScene of the scene being rendered

  • game - the RezGame instance

Substitution Filters

If all we could do was return the attribute values of functions then expressions wouldn’t be very useful. Filters, inspired by Liquid, let us manipulate values into the content we want to display.

For example, let’s say we wanted to capitalize the players name:

content: ```Your name is ${player.name | capitalize}. It is a good name.```

Would render as:

Your name is Matt. It is a good name.

When using a filter you put a pipe symbol | followed by the filter expression which is sometimes just the name of the filter (See the Filter Catalog for a complete list of built-in filters) but can also include parameters.

content: ```The item has the inscription "${item.inscription | trunc: 40}"```

This is an example of a filter that takes parameters. They are separated from the filter name by a colon : and if there is more than one parameter separate them with a comma.

You can also have multiple filters, separating each with a |. For example:

content: ```The book belongs to ${actor.name | prepend: actor.title}.```

might render as:

The book belongs to Mr Sam Spade.

Conditional Templates

The third type of template expression is the conditional template. This allows content to be dynamically included based on an expression. The format of a conditional template is:

$if(expression) {%
  ...true path template content...
%}

or

$if(expression) {%
  ...true path template content...
%}, {%
  ...false path template content...
%}

In the game example above we used:

$if(scene.played > 0) {%...%}

To determine whether to show the won/lost percentage template content. You can nest conditional templates inside other conditional templates.

Iteration Templates

The fourth type of template expression is an iterator template. This allows content to be created from a list of values (In Javascript terms, anything that could be an treated as an array). The format of an iterator template is:

$foreach(x: list) {%
  <div id="${x.id}">${x.title}</div>
%}

This will iterate over the binding list and run the template expression once for each element of list binding x to that element.

$foreach(x: list) {%
  <div id="${x.id}">${x.title}</div>
%}, {%
  <hr />
%}

This alternate form accepts an optional second template expression. This expression will be rendered between each rendering of the content expression.

Note that the list binding should either be an object in the bindings or a property of an object in bindings. You cannot use arbitrary expressions. If you need to use an arbitrary expression use a function binding, so instead of:

content: ```
$foreach(x: a.b.map((el) => somefun(el))) {%
  <div id="${x.id}">${x.title}</div>
%}
````

you would write:

bindings: [
  list: ^{a.b.map((el) => somefun(el))}
]
content: ```
$foreach(x: list) {%
  <div id="${x.id}">${x.title}</div>
%}
```

Partial Templates

A fairly common requirement when building dynamic interfaces is to want to render one card within another. For simple cases you can use the blocks: attribtue on @game, @scene, or @card to render a named card as a block.

However there are two areas where this approach does not work:

  • you don’t know the name of the card to render at author time

  • you want to render the same card multiple times and get a different output

The latter is the $foreach case.

Solving these two problems are what the $partial expression is for.

Do Blocks

To setup attributes for rendering you can run code in an event handler. For example a @card can have an on_start hander:

@card test_card {
  content: ```
  $if(card.show_section) {%
    stuff goes here
  %}
  ```
  on_start: (card) => {
    card.show_section = Math.random() < 0.5;
  }
}

However in many cases it might be easier to use a "do block" inline in the template:

@card test_card {
  content: ```
  $do{
    $card.show_section = Math.random() < 0.5;
  }
  $if(card.show_section) {%
    stuff goes here
  %}
  ```
}

User Components

Sometimes you want to be able to hide away some of the complexity of what you are doing behind a clean syntax. That is what @components are for.

For example let’s say we are writing a lot of event buttons like this:

` <button class="button is-small" data-event="reload">…</button> `

The boilerplate to make a button can be a bit tiresome and it obscures the most important thing about this button, that pressing it triggers the reload event. Let’s make a component:

` @component event_button (bindings, assigns, content) ⇒ { return `<button class="button is-small" data-event="${assigns["event"]}">${content}</button>; } ``

Now we can write:

` <event_button* event="reload">…</event_button*> `

And things are neater. Note that the component tag has a * suffix which is what Rez uses to identify event_button as a user component and look for its @component definition.

Because <event_button*> is a container tag it has a content argument containing all the rendered content that goes inside the tag. For self-closing tags content is undefined.

Behaviour Trees

In the realm of game development and interactive simulations it is very desirable to be able to create entities that can respond to their environment in realistic and complex ways. Rez enables authors to infuse characters and objects with dynamic behaviors using Javascript. However, as the complexity of these behaviors grows, managing them can become problematic. This is where behaviour trees come into play, offering a structured yet flexible way to design and implement AI behaviors.

Behaviour trees are an artificial intelligence technique that revolutionized NPC behavior in video games, with their roots tracing back to landmark titles like Halo 2. Behaviour trees provide a modular, scalable, and easy-to-understand approach. They excel in managing complex decision-making processes, making them an ideal choice for developers looking to create nuanced AI behaviors without getting lost in a web of code.

At the heart of behaviour trees lies the concept of breaking down behaviors into a tree of decisions, where each node in the tree represents some kind of choice or action. These choices and actions guide the entity’s behavior based on conditions and events in the game world. This hierarchical structure allows for clear and logical organization of behaviors, from simple actions like moving to a location, to complex sequences of decisions such as engaging in combat or solving puzzles.

The beauty of using behaviour trees in Rez lies in their versatility and ease of integration. With Rez’s support for behaviour trees, authors can create rich, adaptive, AI that can handle a wide range of scenarios, reacting to the game world and player actions in realistic ways.

In this section, we’ll dive deep into the fundamentals of behaviour trees, explore their syntax and structure within Rez, and provide practical examples to illustrate how they can be implemented to bring your game’s characters and world to life

Behaviour Tree Syntax

Let’s start with an example of the syntax and main concepts:

@actor sam_spade {
  behaviours: ^[$select
      [$sequence
        [actor_in actor=#sam_spade location=#sams_office]
        [item_in item=#whisky_bottle location=#sams_office]
        [actor_drinks actor=#sam_space item=#whisky_bottle]]
      [$sequence
        [actor_in actor=#sam_spade location=#gutmans_suite]
        [actor_in actor=#kasper_gutman location=#gutmans_suite]
        [actor_wisecracks]]]
}

At a high-level this behaviour tree defines some behaviour for our NPC Sam Spade. When Sam’s in his office he will attempt to have a drink. If he finds himself with Kasper Gutman he will attempt a wisecrack. While this is a contrived example it will serve to highlight the main concepts at work.

Behaviour trees are attribute values. In the example above the behaviours: attribute contains a behaviour tree. They are written in the form of a list but have a special ^ prefix to distinguish them from regular lists.

A behaviour tree is composed of behaviours, which may contain other behaviours as children, and where each behaviour is written as a list in the form:

[behaviour options* children*]

So we have the name of the behaviour, followed by zero or more options, followed by zero or more children. In the example the first behaviour is a $select with no options ($select doesn’t take any) and two children:

[$select child1 child2]

The first child is a $sequence behaviour that again has no options ($sequence doesn’t take any either) and three children:

[$sequence child1 child2 child3]

and its first child is an actor_in:

[actor_in actor=#sam_spade location=#sams_office]

This behaviour has two options actor and location and no children. The other two children of the $sequence have a similar structure.

The name of the behaviour corresponds to the id of a @behaviour element in the game. The $ prefix tells us that $select and $sequence are a core behaviours that is @behaviour elements defined in the Rez stdlib and always available to any game.

Options are written as <option name>=<value> where the option name follows the Javascript variable naming pattern the same as attribute names, and the value can be any legal Rez value type.

The behaviours actor_in, item_in, and actor_drinks are not core behaviours but examples of author defined behaviours. As an author your job is to create behaviours that are meaningful in your games context.

Now let’s talk about what all of this means.

Although behaviour trees are syntactically written as a list, they form a branching tree structure as each behaviour can nest other behaviours in its child list, to an arbitrary depth. The first behaviour is the root behaviour of the tree.

When we "execute the tree" it means excuting the root behaviour which in turn may execute some or all of its children until execution reaches the leaves of the tree. But, before we can understand all that, there are some other concepts we need to be aware of.

Success and Failure

When we talk about executing a behaviour tree what we are really saying is: when we execute the root behaviour of the tree does it succeed or fail?

At the level of the tree itself, if the root behaviour succeeds it means that some action was successfully taken in response to whatever event caused us to execute the tree in the first place.

In the context of a behaviour itself success or failure has different meaning. For example, a $select will succeed if any of its children succeed, while a $sequence will only succeed if all of its children succeed (the observant may notice a relation to boolean logic here).

To put this in context, $select can be used to choose among a range of behaviours (given as its children). If one child fails, the next is tried, and so on. If the $select succeeds we know that one of its children succeeded. Conversely the $select failing tells us that none of its children succeeded. By contrast the $sequence is a step-by-step procedure where if any step fails, the $sequence fails.

You will see this pattern of $select/$sequence for selecting among options that are described as a procedure (that may themselves contain other such patterns) often.

$sequence and $select are examples of composite behaviours. We’ll talk about behavior types in a moment.

The 'leaf' behaviours in a tree don’t have children and their success or failure it more directly tied to the state of the game world. In our example above the behaviour:

[actor_in actor=#sam_spade location=#sams_office]

Will, we supppose, succeed if Sam is in his office and fail otherwise. The actor_drinks example is a little more subtle.

[actor_drinks actor=#sam_spade item=#whisky_bottle]

In our example we have already tested that the whisky item is in Sam’s location we can assume that actor_drinks is going to succeed. But this doesn’t always have to be the case. Whether or not a behaviour succeed automatically if its prior conditions succeed is down to you.

Behaviour Types

There are four different types of behaviour:

  • composite

  • decorator

  • condition

  • action

Composite Behaviours

A composite behaviour always has at least one child and can be thought of as a kind of coordinator where its success or failure is based on what happens when it executes some, or all, of its children.

Two of the most important composite behaviours are $sequence and $select.

As you’ve seen, $sequence executes its children in turn until either they have all succeeded (in which case $sequence succeeds) or until one of them fails (at which point $sequence fails). So a sequence general consists of a set of conditions and then the actions that should arise if they are met.

The $select behaviour executes its children in turn until one of them succeeds (at which point $select succeeds) or they have all failed (at which point $select fails). This means that $select is a good way of choosing between a set of alternatives, for example different ways to achieve a goal where the AI can have fall back tactics should any particular behaviour fail or not be available.

You may have observed that $select implements OR-logic while $sequence implements AND-logic. Just these two behaviours allow us to create any complexity of decision-making structure we want.

Decorator Behaviours

A decorator behaviour usually has a single child and its purpose is to modify the result of running its child behaviour.

For example the $invert decorator executes its child and flips the result making success into failure (and vice verca).

Condition Behaviours

A condition behaviour tests the state of the game world and succeeds or fails based upon that test. For example we defined two conditions in the example above actor_in and item_in which test whether an actor, or item, are in a given location.

We can imagine that actor_in succeeds when the specified actor is in the specified location and fails otherwise.

As an author you will create condition behaviours that test things that are meaningful to your game and these behaviours become available to your behaviour trees.

Action Behaviours

An action behaviour modifies the state of the game world. Actions are usually expected to succeed (because, typically, they are placed after conditions that gate whether they should happen or not).

Typically an action will succeed (because the conditions for success should already have been met) although this is not a hard and fast rule.

However be careful about making changes to world state and then failing. This could have unintended consequences.

Putting It All Together

Our first example of a simple behaviour tree uses one composite behaviour ($sequence), two conditions (actor_in, item_in), and one action (actor_drinks).

Let’s make a more complex example:

@actor sam_spade {
  behaviours: ^[
    [$select
      [$sequence
        [actor_sense_danger]
        [$select
          [$sequence
            [actor_is_armed]
            [actor_draws_gun]]
          [$sequence
            [actor_sees_item type=:weapon]
            [actor_equips_item]]
          [$sequence
            [actor_sees_exit]
            [actor_escapes]]]]
      [$sequence
        [actor_thirsty]
        [$select
          [$sequence
            [actor_sees_item type=:drink]
            [actor_equips_item]
            [actor_drinks]]
          [$sequence
            [actor_says msg="Boy I could use a drink!"]]]]]]
}

In this example we’re using $select to choose between 2 high level behaviours (respond to threat, respond to thirst) with each of those behaviours being composed of further $sequence and $select based behaviours.

For example if the player senses danger, do they have a gun? Is there a weapon in their environment? Can they escape?

We’ve made up a bunch of condition and action behaviours like actor_is_armed and actor_says and hand waved over the detail of their implementation.

As an author most of your work will be creating meaningful conditions and actions that represent the state and actions available in your game world.

You’ll note in this example we have a number of actor_xxx behaviours but haven’t specified an actor=xxx option. In this context we’d probably put the actor into working memory.

Working Memory

When a behaviour tree gets executed (using RezBehaviour.executeBehaviour(wmem)) it is passed an object to represent the "working memory" of the tree. This object is passed from behaviour to behaviour and can be used to communicate information between behaviours.

In our example above we might initialize working memory with something like:

{self: $("sam_spade")}

Now any behaviour that needs it can refer to self in the working memory to find out who is performing these behaviours. Working memory can be a useful way to pass information around that is either dynamic (and therefore difficult to refer to as an option) or repetitive.

Core Behaviours

The Rez stdlib defines a range of core behaviours whose id have a $ prefix to distinguish them from author written behaviours. The implementation of the core behaviours is in stdlib.rez.

The core behaviours are intended to provide an overall structure for creating different kinds of behaviour. In particular look at behaviours like $either, $random_choice and $random_each which can introduce variability into behaviour patterns.

Writing Your Own Behaviours

The @behaviour element allows you to write your own behaviours, typically these will be conditions and actions that query & modify, respectively, your game world. Let’s take a look at a query first:

@behaviour actor_is_armed {
  execute: function(owner, behaviour, wmem) {
    return {
      success: ($player.main_inventory.weapon_contents.length > 0),
      wmem: wmem
    }
  }
}

First note that the only thing we are required to define is the execute: attribute, which must be a regular function (an arrow function will not do). All execute: handler functions are required to return an object with two keys: success and wmem. The value for success should a boolean. In this case we return true if the players weapon slot has something in it, false if not. The value for wmem should be the working memory we were passed in.

In this example we’ve assumed the actor is the #player. But we could make it more flexible using an option.

@behaviour actor_is_armed {
  options: ["actor"]
  execute: function(owner, behaviour, wmem) {
    const actor = $(actor);
    return {
      success: (actor.main_inventory.weapon_contents.length > 0),
      wmem: wmem
    }
  }
}

Now we’d specify the behaviour as

^[actor_is_armed actor=#sam_spade]

An as long as Sam’s @actor definition contained a main_inventory all would be well.

Now let’s examine how we would define an action:

@behaviour actor_drinks {
  execute: function(owner, behaviour, wmem) {
    $player.drunk += 1;
    return {
      success: true,
      wmem: wmem
    };
  }
}

We can see that this is even simpler. Most actions will return a successful result since you have, likely, already queried whether they be executed or not. But if an action can be executed and fail you can return a different results as per the query above.

Working memory is assumed to be an object that is passed between the different behaviours in the tree. For example one behaviour could store a value that a later behaviour will use. We can see how actor_sees_item could return success and store the id of the item in the working memory for actor_equips_item to put into their inventory.

Lastly the owner is the object that owns the behaviour tree. This allows you to make use of the owner’s attributes within the behaviour. Here’s an example of doing this. Let’s create a generic query behaviour that lets us test a property of any object and compare it with an attribute of the owning element:

@behaviour query {
  options: ["if", "obj"]
  execute: function(owner, behaviour, wmem) {
    const f = behaviour.option("if").bind(owner);
    const o = $(behaviour.option("obj"));
    return {
      success: f(o),
      wmem: wmem
    };
  }
}

@object foo {
  cost: 2
}

@object bar {
  cash: 10

  test: ^[query obj="foo" if=^{obj.cost < this.cash}]
}

Might take you a while to see what is going on here but we are making use of bind to make this into the owner object within the context of the code block being passed to the query via its if option.

Using Behaviour Trees

Having defined a behaviour tree, how do you use it?

Remember that a behaviour tree is defined as an attribute on an element:

@actor sam_spade {
  behaviours: ^[...]
}

At a basic level we can run this behaviour tree using:

const result = $("sam_spade").behaviours.executeBehaviour({});

The executeBehaviour() method returns an object:

{
  success: true|false,
  wmem: <modified working memory>

}
if(result.success) {

} else {

}

The authors are still experimenting but a broad approach suggests itself:

Behaviours are likely to fall into broad categories that form a response to an event. An example might be "new day" or "player enters location".

You could then define, e.g., a new_day_behaviours: attribute any element that should respond to the :new_day event. Then use a @system to reponds to the event, running the behaviours for all relevant elements.

@system new_day_system {
  after_event: (system, event, result) => {
    if(event === "new_day") {
      const wmem = {};
      $game.getObjectsWith("new_day_behaviours").forEach((el) => {
        el.new_day_behaviours.executeBehaviour(wmem);
      });
    }
    return result;
  }
}

Another approach is to define a behaviour tree to cover all events and use $select to decide which branch to follow. For example:

@actor sam_spade {
  behaviours: ^[select
    [$sequence
      [event is=:new_day]
      [...]]
    [$sequence
      [event is=<some other event>]
      ...]]
}

Now you could define a system for running all behaviours in response to any event:

@system event_behaviours {
  after_event: (system, event, result) => {
    const wmem = {event: event};
    $game.getObjectsWith("behaviours").forEach((el) => {
      el.behaviours.executeBehaviour(wmem);
    })
  }
}

The behaviour system is quite flexible and can be made to work in a number of different ways to suit the needs of your game and ease of authorship.

Differences To Other Systems

Much of the writing on behaviour trees comes from a real-time video game perspective where the trees are used to power enemy-AI. This introduces a number of constraints for example that the behaviour AI must run within one frame (i.e. <0.04s). The "running" status can be used to "break up" a behaviour.

In the Rez context this is less relevant so we do not support the "running" status. However it may be implemented later if it proves useful.

From a terminology perspective we use "executing" instead of the more common "ticking" language. We think it’s more natural to say you are executing a behaviour than ticking one.

We use the term "behaviour" to refer to our behaviour types while you may see them referred to as "nodes" elsewhere. Node is a more mathematical term, we think behaviour is more natural.

Also where you see fallback as a type of behaviour we call it $select. The behaviour is ultimately the same.

Custom Scripts & Styles

Rez supports the addition of custom Javascript & CSS in a number of different ways.

Script & Stylesheet Directives

The @script and @style directives allow embedding arbitrary Javascript or CSS classes into your game.

@script {
  function identifyParrot(p) {
    if(p === "parrot") {
      return "Sqwauk";
    } else {
      return "Pfffft";
    }
  }
}
@stylesheet {
  /* https://gist.github.com/JoeyBurzynski/617fb6201335779f8424ad9528b72c41 */
  .main {
    max-width: 38rem;
    padding: 2rem;
    margin: auto;
  }
}

The contents of these directives is automatically inserted into an appropriate spot in the game files.

Patching Javascript

Another way to include your own Javascript is through the use of the @patch directive which allows you to add new methods to existing JS classes. Here is an example from the stdlib.

@patch ARRAY_FY_SHUFFLE {
  %% Fisher-Yates Shuffle impl from: https://sebhastian.com/fisher-yates-shuffle-javascript/
  patch: "Array"
  method: "fy_shuffle"
  impl: function() {
    let idx = this.length;
    while(--idx > 0) {
      const rand_idx = Math.floor(Math.random() * (idx+1));
      [this[rand_idx], this[idx]] = [this[idx], this[rand_idx]];
    }
    return this;
  }
}

This adds a new method fy_shuffle to Javascript Array instances. So you can now write:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].fy_shuffle()
=> [3, 7, 6, 8, 4, 9, 1, 2, 5, 10]

To add a method to instances use the method: attribute and specify the method name. To add a function to a constructor use the function: attribute instead.

Write Your Own Filters

A third way to include custom Javascript is by implementing a template expression filter. Here is an example from the stdlib:

@filter STRING_STARTS_WITH_FILTER {
  %% String -> Bool

  name: "starts_with"
  impl: (s, search) => {return s.startsWith(search);}
}